attr_accessor 是幹嘛的?
先說結論:
attr_accessor 會幫你在 Ruby 的類別裡產生一對 getter 以及 setter 方法。
不過我想這結論對新手來說有講跟沒講一樣,讓我們繼續往下看。
跟別的程式語言比起來,Ruby 可以省略很多東西,像是呼叫方法的時候可以不用小括號,回傳資料的時候有時候不用特別加 return
。
我們先來看一段範例:
class Girl
def initialize(age)
@age = age
end
end
mary = Girl.new(20)
這是一個簡單的 Ruby 類別,我用 Girl
類別建一個名為 mary
的實體(instance),並且在初始化的時候就設定她的年紀為 20。Girl
類別裡有一個 @age
這個實體變數(instance variable),也許你會猜說如果要知道 mary
的年紀的話,只要:
puts mary.age
就行了,但一執行就會發現錯誤訊息:
undefined method `age' for #<Girl:0x007f93a609fa10 @age=20> (NoMethodError)
怪了,我是想要存取 age
這個屬性,為什麼錯誤訊息卻是 undefined method
?
在解釋之前,要先說明幾個 Ruby 這個程式語言跟別家程式語言在設計上的不同:
一、Ruby 的方法呼叫,常常會適時的省略小括號:
舉個例子來說:
def say_hi_to(name)
puts "hi, #{name}"
end
say_hi_to("eddie")
但我們通常會寫成:
say_hi_to "eddie"
在呼叫方法的時候省略小括號,這在 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 當做參數傳給它。
好啦,既然知道它們都是方法,那要怎麼定義它們呢?
class Girl
def initialize(age)
@age = age
end
def age
return @age # 這個 return 通常會省略
end
def age=(new_age)
@age = new_age
end
end
上面這段範例中,第 6 ~ 8 行的方法會回傳 @age
,又常稱之 getter;第 10 ~ 12 行的方法它會設定 @age
的值,故又常稱之 setter。
不過...等一下! 為什麼方法後面有個等號?
請把等號當做一般的字母看待。在 Ruby 定義方法的時候,等號跟其它字元一樣都可以是方法名字的一部份,只是這個特殊字元必須要放在方法名稱的結尾(其實包括問號跟驚嘆號也都一樣)。
這個方法就叫做 age=
,要使用它就是用 age=(18)
,沒錯,就是連等號一起呼叫它。所以,其實標準形態應該長這樣:
mary.age=(18)
又,因為前面提到,Ruby 可以省略小括號,所以可寫成:
mary.age=18
然後,Ruby 又有幫忙加了一些語法糖衣,讓你在中間加一些空白字元也是可以的:
mary.age = 18
最後就會讓你看起來像是在設定 age 屬性了。
這...會不會太麻煩了點?
照這樣說,如果每次想要用類似的屬性寫法,就必須要寫一對方法來回傳、設定,會不會有點太麻煩啊。
是的,就是要這麼麻煩。不過請放心,工程師都很懶的,所以有另外設計了幾個方法可以讓你快速的產生前面提到的 getter/setter。
如果你的 getter/setter 很單純,就只是有回傳、設定實體變數的話,那你可用 Ruby 內建的幾個方法:attr_reader
、attr_writer
以及 attr_accessor
:
class Girl
attr_accessor :age
def initialize(age)
@age = age
end
end
mary = Girl.new(20)
puts mary.age #=> 20
mary.age = 18
puts mary.age #=> 18
其中,attr_reader
只會幫你產生 getter,attr_writer
只會幫你產生 setter,而 attr_accessor
則會幫你產生 getter 及 setter。如果不相信,可以執行下面這行看一下:
p Girl.instance_methods(false)
應該會看到以下結果:
[:age, :age=]
的確是產生了兩個方法,分別是 age
以及 age=
。
用了 attr_accessor 還能自己寫 getter 或 setter 嗎?
當然是可以的,例如:
class Girl
attr_accessor :age
def age=(new_age)
@age = (new_age > 18) ? 18 : new_age # 如果大於 18,就只設定 18..
end
end
mary = Girl.new
mary.age = 30 # 即使設定為 30 歲...
puts mary.age # 還是會永保 18 歲 :)
因為你重新定義了 age=
方法,在執行的時候 Ruby 會跳出來跟你碎碎唸說 warning: method redefined; discarding old age=
,但程式還是可執行。
所以,如果你只是想要客制化自己的 getter 或 setter 的話,可將 attr_accessor
改為 attr_reader
或 attr_writer
,就不會有這個警告訊息了。
以上,希望對大家有幫助 :)
工商服務
實體課程:Ruby on Rails 實戰課程
線上課程:五倍學院線上課程