甚麼是存取器?
我們曾在之前的章節討論過實例變數,但尚未深入探討。物件的實例變數即是物件的屬性 (attribute),用以區分出同類別的其他物件。能夠讀寫這些屬性非常重要,若要讀寫便需要稱為屬性存取器 (attribute accessors) 的公開方法(編註:所有實例變數都不公開的,除非透過公開方法存取)。我們等一下就會看到,並不是每次都需要明確地寫出存取器方法,現在先看一下所有的步驟。兩種存取器分別是寫入器 (writer) 及閱讀器 (reader)。
| def set_kind(k) # 寫入器
| @kind = k
| end
| def get_kind # 閱讀器
| @kind
| end
| end
nil
ruby> f1 = Fruit.new
#<Fruit:0xfd7e7c8c>
ruby> f1.set_kind("peach") # 使用該寫入器
"peach"
ruby> f1.get_kind # 使用該閱讀器
"peach"
ruby> f1 # 檢驗該物件
#<Fruit:0xfd7e7c8c @kind="peach">
很簡單吧,我們可以儲存取回想查看的水果資料, 但方法名稱有點冗長。以下例子就比較精簡方便:
| def kind=(k)
| @kind = k
| end
| def kind
| @kind
| end
| end
nil
ruby> f2 = Fruit.new
#<Fruit:0xfd7e7c8c>
ruby> f2.kind = "banana"
"banana"
ruby> f2.kind
"banana"
inspect
方法
先離題一下, 現在你會注意到,我們要直接查看物件時,就會出現 #<anObject:0x83678>
這個難懂的訊息。這只是個預設的行為,我們可以隨意變更。只要加上稱為inspect
的方法, 就會傳回以合理方式描述物件的字串,包括物件部分或所有實例變數的狀態。
| def inspect
| "a fruit of the #{@kind} variety"
| end
| end
nil
ruby> f2
"a fruit of the banana variety"
一個相關的方法稱為 to_s
(轉換為字串),於輸出物件時使用。一般來說,你可以將inspect
想像為編寫程式並用以除錯的工具,而 to_s
則是重新定義程式輸出的方式。eval.rb
顯示結果時就會使用inspect
。你可使用 p
方法,輕易地從程式取出除錯輸出。
p anObject
puts anObject.inspect
簡單使用存取器
因為不少實例變數都需要存取器方法,因此 Ruby 提供了一些快捷的方式。
快捷方式 | 效果 |
attr_reader :v | def v; @v; end |
attr_writer :v | def v=(value); @v=value; end |
attr_accessor :v | attr_reader :v; attr_writer :v |
attr_accessor :v, :w | attr_accessor :v; attr_accessor :w |
讓我們善用這些方式加到程式裡吧。首先我們需要一個自動產生出來的閱讀器及寫入器,然後加入新的資訊至 inspect
:
| attr_accessor :condition
| def inspect
| "a #{@condition} #{@kind}"
| end
| end
nil
ruby> f2.condition = "ripe"
"ripe"
ruby> f2
"a ripe banana"
更多水果的樂趣
如果沒有人吃成熟的水果,那就讓時間來解決吧。
| def time_passes
| @condition = "rotting"
| end
| end
nil
ruby> f2
"a ripe banana"
ruby> f2.time_passes
"rotting"
ruby> f2
"a rotting banana"
開始前,我們先要說明一個問題。如果我們現在創造第三件水果,那會怎麼樣呢?要記住,指派值予實例變數後,實例變數才會存在。
ERR: failed to convert nil into String
這是因為 inspect
方法發出問題,而且問題很明顯。我們要求它回報水果的種類及狀態,但是f3
還未獲指派任何屬性。如果我們想的話,可以重新編寫 inspect
方法,讓它可以用 defined?
方法測試實例變數,並只在實例變數存在時才回報,但這樣就可能不是很實用,因為每種水果都有他們的種類與狀態,我們就要確保它們都已經定義。這將在下一節中討論。