Ruby 中的「attr_accessor」
先說結論:
attr_accessor 會幫你在 Ruby 的類別裡產生一對 getter 以及 setter 方法。
不過我想這結論對新手來說有講跟沒講一樣,讓我們繼續往下看。
跟別的程式語言比起來,Ruby 可以省略很多東西,像是呼叫方法的時候可以不用小括號,回傳資料的時候有時候不用特別加 return
。
我們先來看一段範例:
1 2 3 4 5 6 7 |
|
這是一個簡單的 Ruby 類別,我用 Girl
類別建一個名為 mary
的實體(instance),並且在初始化的時候就設定她的年紀為 20。Girl
類別裡有一個 @age
這個實體變數(instance variable),也許你會猜說如果要知道 mary
的年紀的話,只要:
1 |
|
就行了,但一執行就會發現錯誤訊息:
undefined method `age' for #<Girl:0x007f93a609fa10 @age=20> (NoMethodError)
怪了,我是想要存取 age
這個屬性,為什麼錯誤訊息卻是 undefined method
?
在解釋之前,要先說明幾個 Ruby 這個程式語言跟別家程式語言在設計上的不同:
一、Ruby 的方法呼叫,常常會適時的省略小括號:
舉個例子來說:
1 2 3 4 5 |
|
但我們通常會寫成:
1 |
|
在呼叫方法的時候省略小括號,這在 Ruby 是很常見的寫法。
二、Ruby 並沒有「屬性」(property/attribute)這樣的東西:
雖然 mary
看起來有一個 @age
實體變數,但不表示是可以直接存取的屬性。硬是要用 mary.age
問她年紀,或是要用 mary.age = 18
來設定她的年紀,她都會賞你一巴掌,給你錯誤訊息的。
mary.age
你以為是讀取 mary 上的 age 屬性,但事實上是在執行 mary.age()
方法,只是小括號被省略了。mary.age=18
你以為是設定 mary 的 age 屬性,但事實上是執行 mary.age=(18)
方法,只是小括號被省略了。
在 Ruby 裡,很多東西都跟你看到的不太一樣,例如,你以為 1 + 2
是簡單的數學運算嗎? 其實它是 1.+(2)
,它是對數字物件 1 送了一個 +
的訊息,並且把數字物件 2 當做參數傳給它。
好啦,既然知道它們都是方法,那要怎麼定義它們呢?
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
上面這段範例中,第 6 ~ 8 行的方法會回傳 @age
,又常稱之 getter;第 10 ~ 12 行的方法它會設定 @age
的值,故又常稱之 setter。
不過…等一下! 為什麼方法後面有個等號?
請把等號當做一般的字母看待。在 Ruby 定義方法的時候,等號跟其它字元一樣都可以是方法名字的一部份,只是這個特殊字元必須要放在方法名稱的結尾(其實包括問號跟驚嘆號也都一樣)。
這個方法就叫做 age=
,要使用它就是用 age=(18)
,沒錯,就是連等號一起呼叫它。所以,其實標準形態應該長這樣:
1 |
|
又,因為前面提到,Ruby 可以省略小括號,所以可寫成:
1 |
|
然後,Ruby 又有幫忙加了一些語法糖衣,讓你在中間加一些空白字元也是可以的:
1 |
|
最後就會讓你看起來像是在設定 age 屬性了。
這…會不會太麻煩了點?
照這樣說,如果每次想要用類似的屬性寫法,就必須要寫一對方法來回傳、設定,會不會有點太麻煩啊。
是的,就是要這麼麻煩。不過請放心,工程師都很懶的,所以有另外設計了幾個方法可以讓你快速的產生前面提到的 getter/setter。
如果你的 getter/setter 很單純,就只是有回傳、設定實體變數的話,那你可用 Ruby 內建的幾個方法:attr_reader
、attr_writer
以及 attr_accessor
:
1 2 3 4 5 6 7 8 9 10 11 |
|
其中,attr_reader
只會幫你產生 getter,attr_writer
只會幫你產生 setter,而 attr_accessor
則會幫你產生 getter 及 setter。如果不相信,可以執行下面這行看一下:
1 |
|
應該會看到以下結果:
1 |
|
的確是產生了兩個方法,分別是 age
以及 age=
。
用了 attr_accessor 還能自己寫 getter 或 setter 嗎?
當然是可以的,例如:
1 2 3 4 5 6 7 8 9 10 11 |
|
因為你重新定義了 age=
方法,在執行的時候 Ruby 會跳出來跟你碎碎唸說 warning: method redefined; discarding old age=
,但程式還是可執行。
所以,如果你只是想要客制化自己的 getter 或 setter 的話,可將 attr_accessor
改為 attr_reader
或 attr_writer
,就不會有這個警告訊息了。
以上,希望對大家有幫助 :)