首页 文章

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

提问于
浏览
484

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

attr_accessor :var
attr_reader :var
attr_writer :var

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

5 回答

  • 22

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

    class Person
      attr_accessor :age
      ...
    end
    

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

    class Person
      attr_reader :age
      ...
    end
    

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

    但是幕后发生了什么?

    如果你写:

    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 _...帮助器,并且必须自己编写访问器,你会编写比你需要的更多的访问器吗?例如,如果只需要读取年龄,您是否还会编写一个允许它写入的方法?

  • 11

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

  • 705

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

    作为一个实际例子:我编写了一个设计程序,您可以将项目放在容器中 . 该项目有 attr_reader :container ,但它没有't make sense to offer a writer, since the only time the item'容器应该更改时,它被放置在一个新的,这也需要定位信息 .

  • 11

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

  • 13

    重要的是要理解访问者限制对变量的访问,而不是对其内容的访问 . 在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>'
    

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

相关问题