Ruby當中的class method和instance method差在哪?
Ruby新手剛開始都知道,class method就是在定義method時加上self,就變成class method。例如:
class Greeting
def hello
"hello"
end
def self.hello
"hello"
end
end
這樣不管我們執行Greeting.hello
還是Greeting.new.hello
都會有一樣的結果,而這兩種method到底有什麼差別呢?
可以想像一個class是一個大團體,裡頭的每一個人都是一個instance,假如我們要對整個團體下命令,就是在執行class method;反之假如是對其中一個人下命令,則就是執行instance method。
概念如下圖:
從Rails來理解
在學習管理Rails的model時,這樣子的概念比較好解釋,假如我需要針對個別的Post來檢視其去掉空白的內容長度,則可以進行以下設定。
class Post < ActiveRecord::Base
def content_length
self.content.tr(" ","").length
end
end
# 實際使用
Post.first.content_length
像這樣的method,我們就會呼叫個別的post出來執行,而不會執行在整個Post
這個class上頭,這種就是instance method的概念。反之,在呼叫個別的post時我們會使用例如Post.first
或是Post.find(1)
這樣的method,這些搜尋用的method就是class method,會套用在整個class上面,而非個別的instance上面。
風格上的差異
我們跳脫Rails來看,平常在設定class時,這兩種method就會有觀念上的差異。因為今天我們用的是Ruby,是一種物件導向的語言,所有的東西都是物件,因此『各司其職』這件事情就顯得非常重要。Nick Sutterer在Are Class Methods Evil文章中也提到,物件導向語言最重要的是不能讓觀念搞混,否則編修和維護的難度都會大幅提高。
儘管Nick Sutterer相當反對使用class method,但才書學淺的本人覺得在初學階段,還是必須將兩者分清楚,並且在適當的情況下使用。至於那些高深的辯論和用法,等到已經變成專家了再來煩惱。
Michael Lomniki在其評論文章當中有提到,使用instance method和class method是風格的問題。例如以下兩種寫法,就完全是兩種不同風格:
# 以instance method為主
class Greeting
def initialize(name)
@name = name
end
def hello
"Hello, #{@name}"
end
def question
"How are you, #{@name}?"
end
end
# 實際執行
greeting = Greeting.new("John")
greeting.hello
greeting.question
以上風格比較符合Rails以及一般認為物件導向的寫法,以下直接用class method來做相同的事情,看看差在哪裡:
# 以class mehtod為主
class Greeting
def self.hello(name)
"Hello, #{name}"
end
def self.question(name)
"How are you, #{name}?"
end
end
# 實際執行
Greeting.hello "John"
Greeting.question "John"
在第一個例子中,由於Greeting.new
預設會執行initialize
這個method,因此一般來說任何變數都是在產生新的instance時帶入,並且藉由instance variable在class當中傳遞。這樣的好處是只要在initialize
當中將變數設定好,接下來就可以寫得非常精簡。
反之,在第二個例子中,所有變數都要帶到method當中,看起來顯得有些多餘,這也是class method一個不小心就會讓class變得很冗長又難讀的原因。但對於有些人來說,寫code時可以直接用class method,就可以省去多一個new
的困擾。
小結
對新手來說,學習Ruby應該都是從Rails起步,因此還是遵照ActiveRecord那種寫法,將class method和instance method的用法完全隔開,各司其職,避免在維護上自己踩到自己的地雷。等較熟練以後,再來看是否有功能可以直接實作成class method。