Ruby使用这种方便的方式通过使用
等键来共享实例变量attr_accessor :var
attr_reader :var
attr_writer :var
如果我可以简单地使用attr_reader
,为什么我会选择attr_writer
或attr_accessor
?有没有像表演(我怀疑)?我想有一个原因,否则他们就不会制作这样的钥匙。
答案 0 :(得分:721)
您可以使用不同的访问者将您的意图传达给阅读代码的人,并且无论他们如何调用公共API,都可以更轻松地编写能够正常工作的类。
class Person
attr_accessor :age
...
end
在这里,我可以看到我可以读写年龄。
class Person
attr_reader :age
...
end
在这里,我可以看到我只能阅读年龄。想象一下,它是由这个类的构造函数设置的,之后保持不变。如果有一个年龄的变异器(作者),并且该类被写成假设年龄,一旦设置,不会改变,那么代码调用该变异器就会产生错误。
但是幕后发生了什么?
如果你写:
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 _...帮助器,并且必须自己编写访问器,你会编写比你需要的更多的访问器吗?例如,如果只需要读取年龄,你还会写一个允许它被写入的方法吗?
答案 1 :(得分:22)
以上所有答案都是正确的; attr_reader
和attr_writer
编写比手动输入它们的缩写方法更方便。除此之外,它们提供比自己编写方法定义更好的性能。有关详细信息,请参阅Aaron Patterson从this talk(PDF)开始的幻灯片152。
答案 2 :(得分:15)
并非所有对象的属性都是从类外部直接设置的。拥有所有实例变量的编写器通常是弱封装的标志,并警告您在类之间引入了过多的耦合。
作为一个实际例子:我编写了一个设计程序,您可以将项目放在容器中。该项目有attr_reader :container
,但提供作者没有意义,因为该项目的容器应该更改的唯一时间是它被放置在新的容器中,这也需要定位信息。
答案 3 :(得分:13)
您并不总是希望可以从类外部完全访问您的实例变量。在很多情况下,允许对实例变量的读访问是有意义的,但写入它可能不会(例如,从只读源检索数据的模型)。在某些情况下,你想要相反,但我想不出任何不是我头脑中的任何设计。
答案 4 :(得分:11)
重要的是要了解访问者限制对变量的访问,而不是对其内容的访问。在ruby中,与其他一些OO语言一样,每个变量都是指向实例的指针。因此,例如,如果你有一个哈希的属性,你将它设置为"只读"你总是可以改变它的内容,但不能改变指针的内容。 看看这个:
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>'
正如您所看到的那样,可以从Hash @a中删除一个键/值对,作为添加新键,更改值,eccetera。但是你不能指向一个新对象,因为它是一个只读的实例变量。