2011-02-18 68 views
440

Ruby有通過使用鍵,如爲什麼使用Ruby的attr_accessor,attr_reader和attr_writer?

attr_accessor :var 
attr_reader :var 
attr_writer :var 

我爲什麼會選擇attr_readerattr_writer共享實例變量,如果我可以簡單地使用attr_accessor這個方便和便捷的方式?有沒有像表現(我懷疑)?我猜這是有原因的,否則他們不會做出這樣的關鍵。

+4

我只希望他們記錄下來。 http://api.rubyonrails.org/?q=attr_reader – Chloe 2014-05-09 03:06:01

+22

@Chloe你看錯了地方!這是一個Ruby方法,而不是* Rails *:http://www.ruby-doc.org/core-2.1.2/Module.html#method-i-attr_reader – 2014-06-30 00:15:29

+0

可能的重複[什麼是attr \ _accessor in Ruby ?](http://stackoverflow.com/questions/4370960/what-is-attr-accessor-in-ruby) – sschuberth 2014-12-11 16:53:18

回答

642

您可以使用不同的訪問器將您的意圖傳達給讀取您的代碼的人,並且可以更輕鬆地編寫無論公用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 _...幫手,和必須自己編寫訪問器,你會寫更多的訪問器嗎?例如,如果只需要閱讀年齡,您是否也會編寫一個允許寫入的方法?

10

您並不總是希望您的實例變量可以從課程外部完全訪問。有很多情況下允許讀取一個實例變量是有意義的,但寫入它可能不會(例如,從只讀源檢索數據的模型)。有些情況下,你想要的是相反的,但我想不出任何不屬於我的頭腦的東西。

+0

簡單易懂 – 2015-07-17 20:38:59

13

並非對象的所有屬性都是從課外直接設置的。爲所有實例變量編寫代碼通常是弱封裝的標誌,並且警告您在類之間引入了過多的耦合。

作爲一個實際的例子:我寫了一個設計程序,將物品放入容器中。這個項目有attr_reader :container,但是提供一個作者是沒有意義的,因爲項目容器應該改變的唯一時間是當它被放入一個新的時候,這也需要定位信息。

20

以上所有答案都是正確的; attr_readerattr_writer比手動輸入他們所要的方法更方便。除此之外,它們比自己編寫方法定義提供更好的性能。欲瞭解更多信息,請參閱由Aaron Patterson提供的從this talkPDF)開始的第152張幻燈片。

9

瞭解訪問者限制對變量的訪問權限,而不是他們的內容是很重要的。在Ruby中,就像其他一些OO語言一樣,每個變量都是一個指向實例的指針。所以,如果你有一個哈希屬性,並且你將它設置爲「只讀」,你總是可以改變它的內容,但不能改變指針的內容。 看看這個:

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

正如你看到的是可能刪除的散列@a一個鍵/值對,如添加新鍵,變化值,eccetera。但是你不能指向一個新的對象,因爲它是一個只讀實例變量。

相關問題