web-dev-qa-db-fra.com

Ruby variable d'instance de classe vs variable de classe

J'ai lu " Quand les variables d'instance Ruby sont-elles définies? " mais je ne suis pas du tout avisé d'utiliser des variables d'instance de classe.

Les variables de classe sont partagées par tous les objets d'une classe, les variables d'instance appartiennent à un seul objet. Il ne reste pas beaucoup de place pour utiliser les variables d'instance de classe si nous avons des variables de classe.

Quelqu'un pourrait-il expliquer la différence entre ces deux et quand les utiliser?

Voici un exemple de code:

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

Je comprends maintenant que les variables d'instance de classe ne sont pas transmises le long de la chaîne d'héritage!

166
Elmor

Variable d'instance sur une classe:

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]

Variable de classe:

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]

Avec une variable d'instance sur une classe (pas sur une instance de cette classe), vous pouvez stocker quelque chose de commun à cette classe sans que les sous-classes ne les obtiennent aussi automatiquement (et vice-versa). Avec les variables de classe, vous avez la possibilité de ne pas avoir à écrire self.class à partir d'un objet d'instance et (lorsque cela est souhaitable), vous bénéficiez également d'un partage automatique dans toute la hiérarchie de la classe.


En les fusionnant dans un seul exemple qui couvre également les variables d'instance sur des instances:

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

Et puis en action:

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] 
250
Phrogz

Je crois que la principale (seule?) Différence est l'héritage:

class T < S
end

p T.k
=> 23

S.k = 24
p T.k
=> 24

p T.s
=> nil

Les variables de classe sont partagées par toutes les "instances de classe" (c'est-à-dire les sous-classes), alors que les variables d'instance de classe sont spécifiques à cette classe uniquement. Mais si vous n'avez jamais l'intention d'étendre votre classe, la différence est purement académique.

30
bioneuralnet

Les variables d'instance #class sont disponibles uniquement pour la méthode de classe et non pour les méthodes d'instance, alors que les variables de classe sont disponibles pour les méthodes d'instance et les méthodes de classe. De plus, les variables d'instance de classe sont perdues dans la chaîne d'héritage, contrairement aux variables de classe.

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
23
Sachin Saxena

Comme d'autres l'ont dit, les variables de classe sont partagées entre une classe donnée et ses sous-classes. Les variables d'instance de classe appartiennent à exactement une classe; ses sous-classes sont séparées.

Pourquoi ce comportement existe-t-il? Eh bien, tout dans Ruby est un objet, même des classes. Cela signifie que chaque classe a un objet de la classe Class (ou plutôt une sous-classe de Class) lui correspondant. (Lorsque vous dites class Foo, vous déclarez réellement une constante Foo et vous lui affectez un objet de classe.) Et chaque objet Ruby peut avoir des variables d'instance afin que les objets de classe puissent avoir des variables d'instance, aussi.

Le problème est que les variables d'instance sur les objets de classe ne se comportent pas vraiment comme vous le souhaitez habituellement. Vous voulez généralement qu'une variable de classe définie dans une superclasse soit partagée avec ses sous-classes, mais ce n'est pas ainsi que fonctionnent les variables d'instance: la sous-classe a son propre objet de classe et cet objet de classe a ses propres variables d'instance. Ils ont donc introduit des variables de classe distinctes avec le comportement que vous êtes le plus susceptible de vouloir.

En d'autres termes, les variables d'instance de classe sont une sorte d'accident de la conception de Ruby. Vous ne devriez probablement pas les utiliser à moins de savoir précisément ce que vous recherchez.

13
Brent Royal-Gordon

Bien qu'il puisse sembler immédiatement utile d'utiliser des variables d'instance de classe, étant donné que les variables d'instance de classe sont partagées entre les sous-classes et qu'elles peuvent être référées à la fois par les méthodes singleton et instance, il existe un inconvénient majeur. Elles sont partagées et les sous-classes peuvent donc modifier la valeur de la variable d'instance de la classe. La classe de base sera également affectée par le changement, qui constitue généralement un comportement indésirable:

class C
  @@c = 'c'
  def self.c_val
    @@c
  end
end

C.c_val
 => "c" 

class D < C
end

D.instance_eval do 
  def change_c_val
    @@c = 'd'
  end
end
 => :change_c_val 

D.change_c_val
(irb):12: warning: class variable access from toplevel
 => "d" 

C.c_val
 => "d" 

Rails introduit une méthode pratique appelée class_attribute. Comme son nom l'indique, il déclare un attribut de niveau classe dont la valeur peut être héritée par des sous-classes. La valeur class_attribute est accessible dans les méthodes singleton et instance, comme dans le cas de la variable d'instance de classe. Cependant, l’énorme avantage que présente class_attribute dans Rails est que les sous-classes peuvent modifier leur propre valeur et n’ont pas d’impact sur la classe parente.

class C
  class_attribute :c
  self.c = 'c'
end

 C.c
 => "c" 

class D < C
end

D.c = 'd'
 => "d" 

 C.c
 => "c" 
0
Donato

Official Ruby FAQ: quelle est la différence entre les variables de classe et les variables d'instance de classe?

La principale différence est le comportement concernant l'héritage: les variables de classe sont partagées entre une classe et toutes ses sous-classes, alors que les variables d'instance de classe n'appartiennent qu'à une seule classe.

D'une certaine manière, les variables de classe peuvent être considérées comme des variables globales dans le contexte d'une hiérarchie d'héritage, avec tous les problèmes liés aux variables globales. Par exemple, une variable de classe peut (accidentellement) être réaffectée par l'une de ses sous-classes, affectant toutes les autres classes:

class Woof

  @@sound = "woof"

  def self.sound
    @@sound
  end
end

Woof.sound  # => "woof"

class LoudWoof < Woof
  @@sound = "WOOF"
end

LoudWoof.sound  # => "WOOF"
Woof.sound      # => "WOOF" (!)

Ou bien, une classe d'ancêtres peut être ultérieurement rouverte et modifiée, avec des effets probablement surprenants:

class Foo

  @@var = "foo"

  def self.var
    @@var
  end
end

Foo.var  # => "foo" (as expected)

class Object
  @@var = "object"
end

Foo.var  # => "object" (!)

Donc, à moins que vous sachiez exactement ce que vous faites et avez explicitement besoin de ce type de comportement, vous feriez mieux d'utiliser des variables d'instance de classe.

0
not a patch