ruby - clases - Variable de instancia: self vs @
variables de clase ruby (6)
Aquí hay un código:
class Person
def initialize(age)
@age = age
end
def age
@age
end
def age_difference_with(other_person)
(self.age - other_person.age).abs
end
protected :age
end
Lo que quiero saber es la diferencia entre usar @age
y self.age
en age_difference_with
method.
@age - es definitivamente la edad de la variable de instancia
self.age: hace referencia a la antigüedad de la propiedad de la instancia.
Escribir @age
accede directamente a la variable de instancia @age
. Escribir self.age
le dice al objeto que se envíe la age
del mensaje, que generalmente devolverá la variable de instancia @age
, pero podría hacer cualquier cantidad de cosas dependiendo de cómo se implemente el método de age
en una determinada subclase. Por ejemplo, puede tener una clase MiddleAgedSocialite que siempre informe que su edad es 10 años más joven de lo que realmente es. O más prácticamente, una clase PersistentPerson podría leer perezosamente los datos de una tienda persistente, almacenar en caché todos sus datos persistentes en un hash.
La diferencia es que está aislando el uso del método de la implementación del mismo. Si la implementación de la propiedad cambiara, por ejemplo, para mantener la fecha de nacimiento y luego calcular la edad en función de la diferencia en el tiempo entre ahora y la fecha de nacimiento, entonces no es necesario cambiar el código según el método. Si utilizó la propiedad directamente, entonces el cambio necesitaría propagarse a otras áreas del código. En este sentido, usar la propiedad directamente es más frágil que utilizar la interfaz proporcionada por la clase.
La primera respuesta es totalmente correcta, pero como un novato relativo, no me fue inmediatamente claro lo que implicaba (¿enviar mensajes a mí mismo? ¿Eh? ...). Creo que un breve ejemplo ayudará:
class CrazyAccessors
def bar=(val)
@bar = val - 20 # sets @bar to (input - 20)
end
def bar
@bar
end
def baz=(value)
self.bar = value # goes through `bar=` method, so @bar = (50 - 20)
end
def quux=(value)
@bar = value # sets @bar directly to 50
end
end
obj = CrazyAccessors.new
obj.baz = 50
obj.bar # => 30
obj.quux = 50
obj.bar # => 50
No hay ninguna diferencia Sospecho que se hizo solo por el valor documental de ver a self.age
y other_person.age
uno cerca del otro.
Supongo que ese uso permite que se escriba un getter real en el futuro, lo que podría hacer algo más complejo que simplemente devolver una variable de instancia, y en ese caso el método no tendría que cambiar.
Pero esa es una abstracción poco probable de la que preocuparse, después de todo, si la implementación del objeto cambió, es razonable cambiar otros métodos, en algún punto una simple referencia dentro del objeto en sí es perfectamente razonable.
En cualquier caso, la abstracción de la propiedad de la age
todavía no explica el uso explícito de self
, ya que simplemente la age
simple también habría invocado el descriptor de acceso.
Tenga cuidado cuando hereda una clase de Struct.new
que es una buena manera de generar un intializador ( ¿Cómo generar el inicializador en Ruby? )
class Node < Struct.new(:value)
def initialize(value)
@value = value
end
def show()
p @value
p self.value # or `p value`
end
end
n = Node.new(30)
n.show()
regresará
30
nil
Sin embargo, cuando elimina el inicializador, regresará
nil
30
Con la definición de clase
class Node2
attr_accessor :value
def initialize(value)
@value = value
end
def show()
p @value
p self.value
end
end
Deberías proporcionar el constructor.
n2 = Node2.new(30)
n2.show()
regresará
30
30