2010-06-11 19 views
41

Que no haya class Example define como:¿Hay alguna manera de crear métodos solo para la instancia de una clase de Ruby desde esa instancia?

class Example 
    def initialize(test='hey') 
    self.class.send(:define_method, :say_hello, lambda { test }) 
    end 
end 

Al llamar Example.new; Example.new consigo un warning: method redefined; discarding old say_hello. Esto, concluyo, debe ser porque define un método en la clase real (que tiene sentido, a partir de la sintaxis). Y eso, por supuesto, sería desastroso si hubiera múltiples instancias de Example con diferentes valores en sus métodos.

¿Hay alguna manera de crear métodos solo para la instancia de una clase desde dentro de esa instancia?

Respuesta

67

Necesita hacer una referencia a la clase singleton de la instancia, la clase que contiene todas las cosas específicas de la instancia y definir el método en ella. En ruby ​​1.8, se ve un poco desordenado. (Si se encuentra una solución más limpia que me haga saber!)

Rubí 1,8

class Example 
    def initialize(test='hey') 
    singleton = class << self; self end 
    singleton.send :define_method, :say_hello, lambda { test } 
    end 
end 

Ruby 1.9, sin embargo, proporciona una manera mucho más fácil en.

Ruby 1.9

class Example 
    def initialize(test='hey') 
    define_singleton_method :say_hello, lambda { test } 
    end 
end 
+0

¡Es bueno saberlo, muchas gracias! –

+1

Me tomé la libertad de editar la respuesta para usar el término 'singleton class' ya que es el nombre oficial. De hecho, Ruby 1.9.2 presenta 'Object # singleton_class' –

+1

@Marc: Oh, bien. Todavía no he tenido mucho tiempo para jugar con 1.9.2. Me alegro de que finalmente alguien se haya decidido por un nombre. – thorncp

38

En primer lugar, un pequeño consejo de estilo:

self.class.send(:define_method, :say_hello, lambda { test }) 

Usted puede hacer esta mirada un poco más agradable mediante el uso de la nueva literal proc en Ruby 1.9:

self.class.send(:define_method, :say_hello, -> { test }) 

Pero usted no necesita eso. Ruby tiene algo llamado bloques, que son básicamente una pieza de código que puede pasar como argumento a un método. De hecho, ya usaste bloques, ya que lambda es solo un método que toma un bloque como argumento y devuelve un Proc. Sin embargo, define_methodya toma un bloque de todos modos, no hay necesidad de pasar de un bloque a lambda, que la convierte en una Proc que pasa a define_method que luego se convierte de nuevo en un bloque:

self.class.send(:define_method, :say_hello) { test } 

Como Ya notado, estás definiendo el método en la clase incorrecta. Está definiendo en la clase Example, ya que dentro de un método de instancia como initialize, self es el objeto actual (es decir ex1 o ex2 en el ejemplo de @ mikej), lo que significa que self.class es ex1 's clase, que es Example. Entonces, estás sobrescribiendo el mismo método una y otra vez.

Esto lleva a la siguiente comportamiento no deseado:

ex1 = Example.new('ex1') 
ex2 = Example.new('ex2') # warning: method redefined; discarding old say_hello 
ex1.say_hello   # => ex2 # Huh?!? 

En cambio, si quieres un método único, es necesario definir en la clase Singleton:

(class << self; self end).send(:define_method, :say_hello) { test } 

Esto funciona según lo previsto:

ex1 = Example.new('ex1') 
ex2 = Example.new('ex2') 
ex1.say_hello   # => ex1 
ex2.say_hello   # => ex2 

En Ruby 1.9, hay un método que hace eso:

define_singleton_method(:say_hello) { test } 

Ahora, esto funciona como usted quiere, pero aquí hay un problema de mayor nivel: este no es el código de Ruby. Es la sintaxis de Ruby, pero no es código Ruby, es Scheme.

Ahora, el esquema es brillante lenguaje y escritura Código de esquema en la sintaxis de Ruby ciertamente no es algo malo que hacer. Se siente fatal al escribir código Java o PHP en la sintaxis de Ruby, o, como fue el caso en una pregunta de StackOverflow ayer, el código de Fortran-57 en la sintaxis de Ruby. Pero no es tan bueno como escribir el código Ruby en la sintaxis de Ruby.

Scheme es un lenguaje funcional. Los lenguajes funcionales usan funciones (más precisamente, cierres de función) para encapsulación y estado. Pero Ruby no es un lenguaje funcional, es un lenguaje orientado a objetos y los lenguajes OO usan objetos para encapsulación y estado.

Por lo tanto, los cierres de funciones se convierten en objetos y las variables capturadas se convierten en variables de instancia.

También podemos llegar en esto desde un ángulo completamente diferente: lo que está haciendo es que está definiendo un método Singleton, que es un método cuyo objetivo es definir el comportamiento que es específico para uno objeto . Pero está definiendo que el método de Singleton cada instancia de la clase, y que está definiendo el mismo método Singleton para cada instancia de la clase. Ya tenemos un mecanismo para definir el comportamiento para cada instancia de una clase: métodos de instancia.

Ambos argumentos provienen de direcciones completamente opuestas, pero que llegan al mismo destino:

class Example 
    def initialize(test='hey') 
    @test = test 
    end 

    def say_hello 
    @test 
    end 
end 
7

Sé que se hizo dos años atrás, pero me gustaría añadir una respuesta más. .instance_eval ayudará a agregar métodos al objeto instancia

string = "String" 
    string.instance_eval do 
     def new_method 
     self.reverse 
     end 
    end 
Cuestiones relacionadas