¿Por qué utilizar attr_accessor, attr_reader y attr_writer de Ruby?
Ruby tiene esta manera práctica y conveniente de compartir variables de instancia usando claves como
attr_accessor :var
attr_reader :var
attr_writer :var
¿Por qué elegiría attr_reader
o attr_writer
si simplemente podría utilizar attr_accessor
? ¿Existe algo parecido al rendimiento (que dudo)? Supongo que hay una razón, de lo contrario no habrían hecho esas llaves.
Puede utilizar los diferentes descriptores de acceso para comunicar su intención a alguien que lea su código y facilitar la escritura de clases que funcionarán correctamente sin importar cómo se llame su API pública.
class Person
attr_accessor :age
...
end
Aquí puedo ver que puedo leer y escribir la edad.
class Person
attr_reader :age
...
end
Aquí puedo ver que solo puedo leer la edad. Imagine que lo establece el constructor de esta clase y luego permanece constante. Si hubiera un mutador (escritor) para la edad y la clase se escribiera asumiendo que la edad, una vez establecida, no cambia, entonces podría producirse un error si el código llama a ese mutador.
Pero ¿qué está pasando detrás de escena?
Si tú escribes:
attr_writer :age
Eso se traduce en:
def age=(value)
@age = value
end
Si tú escribes:
attr_reader :age
Eso se traduce en:
def age
@age
end
Si tú escribes:
attr_accessor :age
Eso se traduce en:
def age=(value)
@age = value
end
def age
@age
end
Sabiendo eso, he aquí otra forma de pensarlo: si no tuviera los ayudantes attr_... y tuviera que escribir los descriptores de acceso usted mismo, ¿escribiría más descriptores de acceso de los que su clase necesita? Por ejemplo, si solo fuera necesario leer la edad, ¿escribiría también un método que permitiera escribirla?
Todas las respuestas anteriores son correctas; attr_reader
y attr_writer
son más convenientes de escribir que escribir manualmente los métodos para los que son abreviaturas. Aparte de eso, ofrecen un rendimiento mucho mejor que escribir usted mismo la definición del método. Para obtener más información, consulte la diapositiva 152 en adelante de esta charla ( PDF ) de Aaron Patterson.
Es importante comprender que los descriptores de acceso restringen el acceso a las variables, pero no a su contenido. En Ruby, como en otros lenguajes OO, cada variable es un puntero a una instancia. Entonces, si tiene un atributo para un Hash, por ejemplo, y lo configura como "solo lectura", siempre podrá cambiar su contenido, pero no el contenido del puntero. Mira este:
> class A
> attr_reader :a
> def initialize
> @a = {a:1, b:2}
> end
> end
=> :initialize
> a = A.new
=> #<A:0x007ffc5a10fe88 @a={:a=>1, :b=>2}>
> a.a
=> {:a=>1, :b=>2}
> a.a.delete(:b)
=> 2
> a.a
=> {:a=>1}
> a.a = {}
NoMethodError: undefined method `a=' for #<A:0x007ffc5a10fe88 @a={:a=>1}>
from (irb):34
from /usr/local/bin/irb:11:in `<main>'
Como puede ver, es posible eliminar un par clave/valor del Hash @a, como agregar nuevas claves, cambiar valores, etc. Pero no puede apuntar a un objeto nuevo porque es una variable de instancia de solo lectura.