web-dev-qa-db-fra.com

Variable d'instance: self vs @

Voici du code:

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

Ce que je veux savoir, c'est la différence entre l'utilisation de @age et self.age dans age_difference_with méthode.

174
sarunw

L'écriture @age accède directement à la variable d'instance @age. L'écriture self.age indique à l'objet de s'envoyer le message age, qui retournera généralement la variable d'instance @age - mais pourrait faire un certain nombre d'autres choses selon la façon dont la méthode age est implémentée dans une sous-classe donnée. Par exemple, vous pourriez avoir une classe MiddleAgedSocialite qui rapporte toujours son âge 10 ans de moins qu'il ne l'est réellement. Ou plus concrètement, une classe PersistentPerson pourrait lire paresseusement ces données à partir d'un magasin persistant, mettre en cache toutes ses données persistantes dans un hachage.

254
Chuck

La différence est qu'elle isole l'utilisation de la méthode de sa mise en œuvre. Si la mise en œuvre de la propriété devait changer - disons pour garder la date de naissance puis calculer l'âge en fonction de la différence de temps entre maintenant et la date de naissance - alors le code selon la méthode n'a pas besoin de changer. S'il utilisait directement la propriété, la modification devrait se propager à d'autres zones du code. En ce sens, l'utilisation directe de la propriété est plus fragile que l'utilisation de l'interface fournie par la classe.

23
tvanfosson

Soyez averti lorsque vous héritez d'une classe de Struct.new qui est un bon moyen de générer un initialiseur ( Comment générer un initialiseur dans 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()

reviendra

30
nil

Cependant, lorsque vous supprimez l'initialiseur, il reviendra

nil
30

Avec la définition de classe

class Node2
    attr_accessor :value
    def initialize(value)
        @value = value
    end
    def show()
        p @value
        p self.value
    end
end

Vous devez fournir le constructeur.

n2 = Node2.new(30)
n2.show()

reviendra

30
30
6
prosseek

Il n'y a aucune différence. Je soupçonne que cela a été fait juste pour la valeur documentaire de voir self.age et other_person.age les uns à côté des autres.

Je suppose que l'utilisation permet à un getter réel d'être écrit à l'avenir, ce qui pourrait faire quelque chose de plus complexe que de simplement renvoyer une variable d'instance, et dans ce cas, la méthode n'aurait pas besoin de changer.

Mais c'est une abstraction improbable à craindre, après tout, si la mise en œuvre de l'objet a changé, il est raisonnable de changer d'autres méthodes, à un moment donné, une simple référence dans l'objet lui-même est parfaitement raisonnable.

Dans tous les cas, l'abstraction de la propriété age n'explique toujours pas l'utilisation explicite de self, car simplement age aurait également appelé l'accesseur.

2
DigitalRoss

La première réponse est entièrement correcte, mais en tant que débutant relatif, je n'ai pas immédiatement compris ce que cela impliquait (envoyer des messages à soi-même? Euh huh ...). Je pense qu'un court exemple aidera:

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
1
blob