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

Ruby提供了这种便捷的方式来共享实例变量

attr_accessor :var
attr_reader :var
attr_writer :var

如果我可以简单地使用attr_accessor,为什么我要选择attr_readerattr_writer ?有没有类似于表现的东西(我对此表示怀疑)?我想这是有原因的,否则他们不会制造这样的钥匙。

325386 次浏览

您并不总是希望实例变量能够从类外部完全访问。在很多情况下,允许读访问实例变量是有意义的,但写入实例变量可能没有意义(例如,从只读源检索数据的模型)。有些情况下,你想要的是相反的,但我想不出任何一种情况不是在我的头脑中。

你可以使用不同的访问器将你的意图传达给阅读你代码的人,并使编写类变得更容易,无论它们的公共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

知道了这一点,这里有另一种思考方式:如果你没有吸引力……helper,并且必须自己编写访问器,你会编写超出类需要的访问器吗?例如,如果只需要读取年龄,您是否也会编写一个允许它被写入的方法?

并非对象的所有属性都可以从类外部直接设置。所有实例变量都有编写器,这通常是弱封装的标志,也是在类之间引入过多耦合的警告。

作为一个实际的例子:我写了一个设计程序,你把物品放在容器里。项目有attr_reader :container,但是提供写入器是没有意义的,因为项目的容器只有在放入新容器时才应该改变,这也需要定位信息。

以上所有答案都是正确的;编写attr_readerattr_writer比手动输入它们的缩略方法更方便。除此之外,它们提供了比自己编写方法定义更好的性能。要了解更多信息,请参阅幻灯片152,从Aaron Patterson的这个演讲 (PDF)开始。

重要的是要理解访问器限制对变量的访问,而不是对变量内容的访问。在ruby中,就像在其他一些OO语言中一样,每个变量都是一个指向实例的指针。因此,如果你有一个哈希属性,例如,你设置它为“只读”;你总是可以改变指针的内容,但不能改变指针的内容。 看这个:

> class A
>   attr_reader :a
>   def initialize
>     @a = {a:1, b:2}
>   end
> end
=> :initialize
> a = A.new
=> #<A:0x007ffc5a10fe88 @a={:a=>1, :b=>2}>
> a.a
=> {:a=>1, :b=>2}
> a.a.delete(:b)
=> 2
> a.a
=> {:a=>1}
> a.a = {}
NoMethodError: undefined method `a=' for #<A:0x007ffc5a10fe88 @a={:a=>1}>
from (irb):34
from /usr/local/bin/irb:11:in `<main>'

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