Question Pourquoi utiliser attr_accessor, attr_reader et attr_writer de Ruby?


Ruby a cette manière pratique et pratique de partager des variables d’instance en utilisant des clés telles que

attr_accessor :var
attr_reader :var
attr_writer :var

Pourquoi devrais-je choisir attr_reader ou attr_writer si je pouvais simplement utiliser attr_accessor? Y a-t-il quelque chose comme la performance (ce dont je doute)? Je suppose qu'il y a une raison, sinon ils n'auraient pas fait de telles clés.


463
2018-02-18 21:34


origine


Réponses:


Vous pouvez utiliser les différents accesseurs pour communiquer votre intention à quelqu'un qui lit votre code et faciliter l'écriture de classes qui fonctionneront correctement, quelle que soit la manière dont leur API publique est appelée.

class Person
  attr_accessor :age
  ...
end

Ici, je peux voir que je peux lire et écrire l'âge.

class Person
  attr_reader :age
  ...
end

Ici, je peux voir que je ne peux lire que l'âge. Imaginez qu'il est défini par le constructeur de cette classe et après cela reste constant. S'il y avait un mutateur (écrivain) pour l'âge et que la classe était écrite en supposant que l'âge, une fois défini, ne change pas, alors un bogue pourrait résulter de l'appel du code par ce mutateur.

Mais que se passe-t-il dans les coulisses?

Si vous écrivez:

attr_writer :age

Cela se traduit par:

def age=(value)
  @age = value
end

Si vous écrivez:

attr_reader :age

Cela se traduit par:

def age
  @age
end

Si vous écrivez:

attr_accessor :age

Cela se traduit par:

def age=(value)
  @age = value
end

def age
  @age
end

Sachant cela, voici une autre façon de penser: si vous n'aviez pas les assistants attr _... et que vous deviez écrire les accesseurs vous-même, écririez-vous plus d'accesseurs que votre classe n'en avait besoin? Par exemple, si l'âge devait seulement être lu, écririez-vous aussi une méthode permettant de l'écrire?


671
2018-02-18 21:43



Toutes les réponses ci-dessus sont correctes. attr_reader et attr_writer sont plus faciles à écrire que de taper manuellement les méthodes pour lesquelles ils sont abrégés. En dehors de cela, ils offrent de meilleures performances que l'écriture de la définition de la méthode vous-même. Pour plus d'informations, voir diapositive 152 et suivantes cette conversation (PDF) par Aaron Patterson.


21
2018-04-27 15:11



Tous les attributs d'un objet ne sont pas censés être directement définis en dehors de la classe. Avoir des auteurs pour toutes les variables de votre instance est généralement un signe d’encapsulation faible et un avertissement indiquant que vous introduisez trop de couplage entre vos classes.

Comme exemple pratique: j'ai écrit un programme de conception dans lequel vous mettez des articles dans des conteneurs. L'article avait attr_reader :container, mais il n’était pas logique d’offrir un rédacteur, car le seul moment où le conteneur de l’article devait changer est celui où il est placé dans un nouveau, ce qui nécessite également des informations de positionnement.


13
2018-02-18 22:28



Vous ne voulez pas toujours que vos variables d'instance soient entièrement accessibles depuis l'extérieur de la classe. Dans de nombreux cas, autoriser un accès en lecture à une variable d’instance a du sens, mais ne pas l’écrire (par exemple, un modèle qui récupère des données à partir d’une source en lecture seule). Il y a des cas où vous voulez le contraire, mais je ne peux pas en penser à ceux qui ne me sont pas apparus.


10
2018-02-18 21:42



Il est important de comprendre que les accesseurs restreignent l'accès aux variables, mais pas à leur contenu. Dans ruby, comme dans d'autres langages OO, chaque variable est un pointeur vers une instance. Ainsi, si vous avez un attribut pour un hachage, par exemple, et que vous le définissez comme étant "en lecture seule", vous pouvez toujours modifier son contenu, mais pas le contenu du pointeur. Regarde ça:

irb(main):024:0> class A
irb(main):025:1> attr_reader :a
irb(main):026:1> def initialize
irb(main):027:2> @a = {a:1, b:2}
irb(main):028:2> end
irb(main):029:1> end
=> :initialize
irb(main):030:0> a = A.new
=> #<A:0x007ffc5a10fe88 @a={:a=>1, :b=>2}>
irb(main):031:0> a.a
=> {:a=>1, :b=>2}
irb(main):032:0> a.a.delete(:b)
=> 2
irb(main):033:0> a.a
=> {:a=>1}
irb(main):034:0> a.a = {}
NoMethodError: undefined method `a=' for #<A:0x007ffc5a10fe88 @a={:a=>1}>
        from (irb):34
        from /usr/local/bin/irb:11:in `<main>'

Comme vous pouvez le voir est possible supprimer une paire clé / valeur du Hash @ a, comme ajouter de nouvelles clés, modifier les valeurs, eccetera. Mais vous ne pouvez pas pointer vers un nouvel objet car il s'agit d'une variable d'instance en lecture seule.


10
2017-07-02 16:10