ChatGPT解决这个技术问题 Extra ChatGPT

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

Ruby 有这种方便快捷的方式来共享实例变量,方法是使用类似的键

attr_accessor :var
attr_reader :var
attr_writer :var

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

What is attr_accessor in Ruby? 的可能重复项

W
Wayne Conrad

您可以使用不同的访问器将您的意图传达给阅读您的代码的人,并使编写无论如何调用其公共 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_... 助手,并且必须自己编写访问器,你会编写比你的类需要更多的访问器吗?例如,如果只需要读取年龄,你会不会也写一个允许它被写入的方法?


编写 attr_reader :adef a; return a; end confreaks.net/videos/… 相比,还有一个显着的性能优势
@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 的性能优势,从而将整体运行时间缩短一秒。
“大概是这个类的构造函数设置的,保持不变。”这是不准确的。读取器的实例变量可能会经常更改。但是,它们的值只能由类私下更改。
您可以使用“,”添加2个以上的属性,例如:attr_accessor :a, :b
这些年来的价值:github.com/JuanitoFatas/… 根据 ruby 2.2.0 attr_* 的最新基准,它比 getter 和 setter 更快。
B
Benjamin Oakes

以上所有答案都是正确的; attr_readerattr_writer 编写起来比手动输入它们是简写的方法更方便。除此之外,它们提供的性能比自己编写方法定义要好得多。有关详细信息,请参阅 Aaron Patterson 从 this talk (PDF) 开始的幻灯片 152。


C
Chuck

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

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


N
Nikita Fedyashev

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

> 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>'

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


c
coreyward

您并不总是希望您的实例变量可以从类外部完全访问。在很多情况下,允许对实例变量进行读取访问是有意义的,但写入它可能没有意义(例如,从只读源检索数据的模型)。在某些情况下您想要相反的情况,但我想不出任何不是我头脑中人为设计的东西。


关注公众号,不定期副业成功案例分享
关注公众号

不定期副业成功案例分享

领先一步获取最新的外包任务吗?

立即订阅