剛開始寫Ruby的時候我們常常會遇到singleton method與singleton class而不自知,本篇文章對這兩個概念做一些簡單的介紹。
Singleton Method
singleton method的定義是:只屬於一個物件的方法。當我們希望某些方法只有特定的物件能夠擁有的時候,便不能夠透過繼承的方式來定義方法,因為如果我們在類別定義方法的話,這個方法便會被以這個類別實例化的物件所繼承。看個例子:
# 只想要obj1有singleton_method_for_obj1方法class MyClass
def singleton_method_for_obj1
"singile_method_for_obj1"
end
endobj1 = MyClass.new
obj2 = MyClass.new
obj1.singleton_method_for_obj1 # singile_method_for_obj1
obj2.singleton_method_for_obj1 # singile_method_for_obj1
由於singleton_method_for_obj1
方法是定義在MyClass
類別中,在上面例子可以看到obj2
也繼承了singleton_method_for_obj1
方法,這不是我們想要的結果。
class MyClass; endobj1 = MyClass.newdef obj1.singleton_method_for_obj1
"obj1.singleton_method_for_obj1"
endobj1.singleton_method_for_obj1 # obj1.singleton_method_for_obj1obj2 = MyClass.new
obj2.singleton_method_for_obj1 # NoMethodError
直接用def obj1.singleton_method_for_obj1
的語法便可以直接創建一個只屬於obj1
的singleton_method。我們可以用Object#singleton_methods
這個方法來檢視物件所含有的singleton methods。
class MyClass; endobj1 = MyClass.newdef obj1.singleton_method_for_obj1
"obj1.singleton_method_for_obj1"
endobj1.singleton_methods # [:singleton_method_for_obj1]
類方法與Singleton Method
對照一下定義singleton method與定義類方法的語法會發現其實他們都是一樣的。在Ruby裡面,類別也是一種實例,因此可以說類方法本身就是一種定義在類別的singleton method。
class MyClass
def self.class_method
"class_method"
end
enddef MyClass.class_method2
"class_method2"
endMyClass.class_method # class_method
MyClass.class_method2 # class_method2
MyClass.singleton_methods # [:class_method2, :class_method]
Singleton Class
既然singleton method不是放在繼承的類別裡面,那會放在哪裡呢?答案是每個物件都有自己的singleton class。
class MyClass; endobj1 = MyClass.newdef obj1.singleton_method_for_obj1
"obj1.singleton_method_for_obj1"
endobj1.singleton_class # #<Class:#<MyClass:0x000055d578fdbf80>>
obj1.singleton_class.superclass # MyClass
上面的程式碼除了用Object#singleton_class
方法來找出obj1
的singleton class之外也看了這個singleton class的superclass,就是obj1
所繼承的類別MyClass
!在繼承的類別尋找方法之前,Ruby會先去物件本身的singleton class找指定的方法,找不到才會到繼承體系上繼續尋找。
s1 = "abc"
def s1.length
"QQ"
ends1.lengths2 = "123" # QQ
s2.length # 3
在上面的例子中,在s1
的singleton class蓋過了原本String
類別的length
方法,因此s1
呼叫length
方法時會優先呼叫我們定義的方法,而s2
就會用原本的length
方法,因為新的length
方法只定義在s1
的singleton class中。
也可以用另一個語法來打開某個物件的singleton class:
class MyClass; endobj1 = MyClass.newsingleton_class_of_obj1 = class << obj1
self
endsingleton_class_of_obj1 == obj1.singleton_class # true
在上面的程式碼中,利用class << obj1
的語法打開了obj1
的singleton class的作用域,再回傳self
,此時的self
便是obj1
的singleton class。
那為何類別方法是可以繼承的?
看一段程式碼:
class C
class << self
def a_class_method
"C.a_class_method"
end
end
endclass D < C; endC.singleton_class # #<Class:C>
D.singleton_class # #<Class:D>
D.singleton_class.superclass # #<Class:C>D.a_class_method # C.a_class_method
由上面的程式碼我們可以看出,D類別的singleton class 的superclass便是C類別的singleton class,這也是為什麼C類別的singleton method(類別方法)會在D類別身上找到!