Why use Ruby's attr_accessor, attr_reader and attr_writer?
Ruby有这样一种简便的方法,可以通过使用键来共享实例变量,比如
1 2 3 | attr_accessor :var attr_reader :var attr_writer :var |
如果我可以简单地使用
您可以使用不同的访问器向阅读代码的人传达您的意图,并使编写类变得更容易,这些类无论如何调用其公共API都能正常工作。
1 2 3 4 | class Person attr_accessor :age ... end |
在这里,我可以看到我既读又写的年龄。
1 2 3 4 | class Person attr_reader :age ... end |
号
在这里,我可以看到我只能读年龄。假设它是由这个类的构造函数设置的,并且在这之后保持不变。如果有一个针对年龄的变异体(编写器),并且类是在假定年龄(一旦设置)不变的情况下编写的,那么一个bug可能是由调用该变异体的代码引起的。
但幕后发生了什么?
如果你写:
1 | attr_writer :age |
翻译成:
1 2 3 | def age=(value) @age = value end |
。
如果你写:
1 | attr_reader :age |
翻译成:
1 2 3 | def age @age end |
。
如果你写:
1 | attr_accessor :age |
。
翻译成:
1 2 3 4 5 6 7 | def age=(value) @age = value end def age @age end |
知道了这一点,这里有另一种思考方法:如果你没有属性…帮助者,并且必须自己编写访问器,您会编写比您的类需要的访问器更多的访问器吗?例如,如果只需要读年龄,你是否也会写一个允许写年龄的方法?
以上答案都是正确的;与手工输入他们的简写方法相比,
并非所有对象属性都是从类外部直接设置的。拥有所有实例变量的编写器通常是弱封装的标志,并且警告您在类之间引入了太多耦合。
作为一个实际的例子:我编写了一个设计程序,您可以在其中将项目放入容器中。这个项目有
您并不总是希望实例变量可以从类外部完全访问。在许多情况下,允许对实例变量进行读访问是有意义的,但写入实例变量可能没有意义(例如,从只读源中检索数据的模型)。有些情况下,你想要的是相反的,但我想不出有什么不是我头脑中虚构出来的。
重要的是要理解访问器限制对变量的访问,而不是其内容的访问。在Ruby中,和其他一些OO语言一样,每个变量都是指向实例的指针。因此,例如,如果您有一个哈希的属性,并且将其设置为"只读",那么您总是可以更改它的内容,但不能更改指针的内容。看看这个:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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中删除一个键/值对,如添加新键、更改值和eccerta。但不能指向新对象,因为它是只读实例变量。