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

Vol*_*ort 506 ruby

Ruby有这种方便易用的方法来使用像这样的键来共享实例变量

attr_accessor :var
attr_reader :var
attr_writer :var
Run Code Online (Sandbox Code Playgroud)

为什么我会选择attr_reader或者attr_writer我可以简单地使用attr_accessor?有没有像表演(我怀疑)?我想有一个原因,否则他们就不会有这样的钥匙.

Way*_*rad 737

您可以使用不同的访问器将您的意图传达给阅读代码的人,并且无论他们如何调用公共API,都可以更轻松地编写能够正常工作的类.

class Person
  attr_accessor :age
  ...
end
Run Code Online (Sandbox Code Playgroud)

在这里,我可以看到我可以读写年龄.

class Person
  attr_reader :age
  ...
end
Run Code Online (Sandbox Code Playgroud)

在这里,我可以看到我可能只阅读年龄.想象一下,它是由这个类的构造函数设置的,之后保持不变.如果有一个年龄的mutator(作者),并且该类被写成假设年龄,一旦设置,不会改变,那么代码调用该mutator会导致错误.

但是幕后发生了什么?

如果你写:

attr_writer :age
Run Code Online (Sandbox Code Playgroud)

这被翻译成:

def age=(value)
  @age = value
end
Run Code Online (Sandbox Code Playgroud)

如果你写:

attr_reader :age
Run Code Online (Sandbox Code Playgroud)

这被翻译成:

def age
  @age
end
Run Code Online (Sandbox Code Playgroud)

如果你写:

attr_accessor :age
Run Code Online (Sandbox Code Playgroud)

这被翻译成:

def age=(value)
  @age = value
end

def age
  @age
end
Run Code Online (Sandbox Code Playgroud)

知道了,这是考虑它的另一种方式:如果你没有attr _...帮助器,并且必须自己编写访问器,你会编写比你需要的更多的访问器吗?例如,如果只需要读取年龄,您是否还会编写一个允许它写入的方法?

  • @Nitrodist,有趣.对于Ruby 1.8.7,`attr_reader`定义的访问器占用手动定义的访问器的86%.对于Ruby 1.9.0,`attr_reader`定义的访问器占用手动定义的访问器的94%的时间.然而,在我的所有测试中,访问器都很快:访问器需要大约820纳秒(Ruby 1.8.7)或440纳秒(Ruby 1.9).在这些速度下,您需要为"attr_accessor"的性能优势调用数百万次访问器,以便将整个运行时间提高一秒. (81认同)
  • 编写`attr_reader:a`与`def a;还有*显着的*性能优势; 返回; 结束`http://confreaks.net/videos/427-rubyconf2010-zomg-why-is-this-code-so-slow (53认同)
  • "据推测,它是由这个类的构造函数设置的,并且保持不变." 这不准确.带读者的实例变量可能会经常更改.但是,它们的值只能由类私有地更改. (21认同)
  • 您可以使用","添加2个以上的属性,例如:`attr_accessor:a,:b` (10认同)
  • 这些年来的价值:https://github.com/JuanitoFatas/fast-ruby#attr_accessor-vs-getter-and-setter-code根据ruby 2.2.0的最新基准attr_ *比getter更快和二传手。 (2认同)

haw*_*awx 23

以上所有答案都是正确的; attr_reader并且attr_writer比手动输入它们的缩写方法更方便.除此之外,它们提供比自己编写方法定义更好的性能.有关更多信息,请参阅Aaron Patterson撰写的本演讲(PDF)中的幻灯片152 .


Chu*_*uck 15

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

作为一个实际例子:我编写了一个设计程序,您可以将项目放在容器中.该项目有attr_reader :container,但提供作者没有意义,因为项目的容器应该更改的唯一时间是它被放置在新的时间,这也需要定位信息.


cor*_*ard 13

您并不总是希望可以从类外部完全访问您的实例变量.有很多情况允许对实例变量进行读访问是有意义的,但写入它可能不会(例如,从只读源检索数据的模型).在某些情况下,你想要相反,但我想不出任何不做的事情.


小智 12

重要的是要理解访问者限制对变量的访问,而不是对其内容的访问.在ruby中,与其他一些OO语言一样,每个变量都是指向实例的指针.因此,例如,如果您具有Hash的属性,并且将其设置为"只读",则始终可以更改其内容,但不能更改指针的内容.看这个:

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>'
Run Code Online (Sandbox Code Playgroud)

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