为什么使用Ruby的attr_accessor,attr_reader和attr_writer?

Ruby有这样一个方便方便的方法来使用键来共享实例variables

attr_accessor :var attr_reader :var attr_writer :var 

如果我可以简单地使用attr_accessor为什么我会selectattr_readerattr_accessor ? 有没有像表演(我怀疑)? 我猜这是有原因的,否则就不会有这样的关键。

您可以使用不同的访问器将您的意图传达给读取您的代码的人,并且使得编写能够正确工作的类更容易,无论他们的公共API如何被调用。

 class Person attr_accessor :age ... end 

在这里,我可以看到我可以读写这个时代。

 class Person attr_reader :age ... end 

在这里,我可以看到,我只能读年龄。 想象一下,它是由这个类的构造函数设置的,之后保持不变。 如果有一个年龄增长的变种人(编者),而且这个class级是在假设年龄的情况下编写的,那么一旦设置,就不会改变,那么调用这个变种人的代码就会产生一个bug。

但幕后发生了什么?

如果你写:

 attr_writer :age 

这被翻译成:

 def age=(value) @age = value end 

如果你写:

 attr_reader :age 

这被翻译成:

 def age @age end 

如果你写:

 attr_accessor :age 

这被翻译成:

 def age=(value) @age = value end def age @age end 

知道了,这里有另一种思考方式:如果你没有attr _…助手,并且必须自己写访问器,你会写更多的访问器吗? 例如,如果年龄只需要阅读,你是否也会写一个方法来写它?

以上所有答案都是正确的。 attr_readerattr_writer比手动input他们所要的方法更方便。 除此之外,他们提供比自己写方法定义好得多的性能。 欲了解更多信息,请参阅Aaron Patterson的演讲 ( PDF )。

并非所有对象的属性都是从课外直接设置的。 为所有的实例variables编写代码通常是一个弱封装的标志,并且警告你在类之间引入了太多的耦合。

作为一个实际的例子:我写了一个devise程序,把物品放在容器中。 该项目有attr_reader :container ,但提供一个作者是没有意义的,因为项目的容器应该改变的唯一时间是当它被放置在一个新的,这也需要定位信息。

您并不总是希望您的实例variables可以从课程外部完全访问。 有很多情况下,允许读取访问一个实例variables是有道理的,但写入它可能不会(例如,从只读源检索数据的模型)。 有些情况下,你想要的是相反的,但我不能想到任何不是我的头顶上做的事情。

了解访问者限制对variables的访问权限,而不是他们的内容是很重要的。 在Ruby中,就像其他一些OO语言一样,每个variables都是一个指向实例的指针。 所以,如果你有一个哈希的属性,例如,你把它设置为“只读”,你总是可以改变它的内容,而不是指针的内容。 看这个:

 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> aa => {:a=>1, :b=>2} irb(main):032:0> aadelete(:b) => 2 irb(main):033:0> aa => {:a=>1} irb(main):034:0> aa = {} NoMethodError: undefined method `a=' for #<A:0x007ffc5a10fe88 @a={:a=>1}> from (irb):34 from /usr/local/bin/irb:11:in `<main>' 

正如你所看到的,可以从Hash @a中删除一个键/值对,如添加新键,更改值,eccetera。 但是你不能指向一个新的对象,因为它是只读的实例variables。