Variable de instancia de clase Ruby frente a variable de clase
Leí ¿Cuándo se configuran las variables de instancia de Ruby? pero tengo dudas sobre cuándo usar variables de instancia de clase.
Las variables de clase son compartidas por todos los objetos de una clase, las variables de instancia pertenecen a un objeto. No queda mucho espacio para usar variables de instancia de clase si tenemos variables de clase.
¿Alguien podría explicar la diferencia entre estos dos y cuándo usarlos?
Aquí hay un ejemplo de código:
class S
@@k = 23
@s = 15
def self.s
@s
end
def self.k
@@k
end
end
p S.s #15
p S.k #23
Actualización: ¡ahora lo entiendo! Las variables de instancia de clase no se pasan a lo largo de la cadena de herencia.
Variable de instancia en una clase:
class Parent
@things = []
def self.things
@things
end
def things
self.class.things
end
end
class Child < Parent
@things = []
end
Parent.things << :car
Child.things << :doll
mom = Parent.new
dad = Parent.new
p Parent.things #=> [:car]
p Child.things #=> [:doll]
p mom.things #=> [:car]
p dad.things #=> [:car]
variables de clase:
class Parent
@@things = []
def self.things
@@things
end
def things
@@things
end
end
class Child < Parent
end
Parent.things << :car
Child.things << :doll
p Parent.things #=> [:car,:doll]
p Child.things #=> [:car,:doll]
mom = Parent.new
dad = Parent.new
son1 = Child.new
son2 = Child.new
daughter = Child.new
[ mom, dad, son1, son2, daughter ].each{ |person| p person.things }
#=> [:car, :doll]
#=> [:car, :doll]
#=> [:car, :doll]
#=> [:car, :doll]
#=> [:car, :doll]
Con una variable de instancia en una clase (no en una instancia de esa clase), puede almacenar algo común a esa clase sin que las subclases también lo obtengan automáticamente (y viceversa). Con las variables de clase, tiene la comodidad de no tener que escribir self.class
desde un objeto de instancia y (cuando sea conveniente) también puede compartir automáticamente en toda la jerarquía de clases.
Fusionarlos en un solo ejemplo que también cubre variables de instancia en instancias:
class Parent
@@family_things = [] # Shared between class and subclasses
@shared_things = [] # Specific to this class
def self.family_things
@@family_things
end
def self.shared_things
@shared_things
end
attr_accessor :my_things
def initialize
@my_things = [] # Just for me
end
def family_things
self.class.family_things
end
def shared_things
self.class.shared_things
end
end
class Child < Parent
@shared_things = []
end
Y luego en acción:
mama = Parent.new
papa = Parent.new
joey = Child.new
suzy = Child.new
Parent.family_things << :house
papa.family_things << :vacuum
mama.shared_things << :car
papa.shared_things << :blender
papa.my_things << :quadcopter
joey.my_things << :bike
suzy.my_things << :doll
joey.shared_things << :puzzle
suzy.shared_things << :blocks
p Parent.family_things #=> [:house, :vacuum]
p Child.family_things #=> [:house, :vacuum]
p papa.family_things #=> [:house, :vacuum]
p mama.family_things #=> [:house, :vacuum]
p joey.family_things #=> [:house, :vacuum]
p suzy.family_things #=> [:house, :vacuum]
p Parent.shared_things #=> [:car, :blender]
p papa.shared_things #=> [:car, :blender]
p mama.shared_things #=> [:car, :blender]
p Child.shared_things #=> [:puzzle, :blocks]
p joey.shared_things #=> [:puzzle, :blocks]
p suzy.shared_things #=> [:puzzle, :blocks]
p papa.my_things #=> [:quadcopter]
p mama.my_things #=> []
p joey.my_things #=> [:bike]
p suzy.my_things #=> [:doll]
Fuente
Disponibilidad para instanciar métodos
- Las variables de instancia de clase están disponibles sólo para los métodos de clase y no para los métodos de instancia.
- Las variables de clase están disponibles tanto para métodos de instancia como para métodos de clase.
Heredabilidad
- Las variables de instancia de clase se pierden en la cadena de herencia.
- Las variables de clase no lo son.
class Vars
@class_ins_var = "class instance variable value" #class instance variable
@@class_var = "class variable value" #class variable
def self.class_method
puts @class_ins_var
puts @@class_var
end
def instance_method
puts @class_ins_var
puts @@class_var
end
end
Vars.class_method
puts "see the difference"
obj = Vars.new
obj.instance_method
class VarsChild < Vars
end
VarsChild.class_method
Creo que la principal (¿única?) diferencia es la herencia:
class T < S
end
p T.k
=> 23
S.k = 24
p T.k
=> 24
p T.s
=> nil
Las variables de clase son compartidas por todas las "instancias de clase" (es decir, subclases), mientras que las variables de instancia de clase son específicas sólo de esa clase. Pero si nunca tienes intención de ampliar tu clase, la diferencia es puramente académica.
Como dijeron otros, las variables de clase se comparten entre una clase determinada y sus subclases. Las variables de instancia de clase pertenecen exactamente a una clase; sus subclases están separadas.
¿Por qué existe este comportamiento? Bueno, todo en Ruby es un objeto, incluso las clases. Eso significa que cada clase tiene un objeto de la clase Class
(o más bien, una subclase de Class
) correspondiente. (Cuando dices class Foo
, en realidad estás declarando una constante Foo
y asignándole un objeto de clase). Y cada objeto Ruby puede tener variables de instancia, por lo que los objetos de clase también pueden tener variables de instancia.
El problema es que las variables de instancia en los objetos de clase realmente no se comportan de la forma en que normalmente se desea que se comporten las variables de clase. Por lo general, desea que una variable de clase definida en una superclase se comparta con sus subclases, pero no es así como funcionan las variables de instancia: la subclase tiene su propio objeto de clase y ese objeto de clase tiene sus propias variables de instancia. Entonces introdujeron variables de clase separadas con el comportamiento que es más probable que desees.
En otras palabras, las variables de instancia de clase son una especie de accidente del diseño de Ruby. Probablemente no deberías usarlos a menos que sepas específicamente que son lo que estás buscando.