Chapter 3: Methods - Part I
Singleton class In Ruby 神秘的匿名者
接下來的主題對剛學 Ruby 程式語言的朋友們來說,應該算是比較進階的題目。其實我已看過了網路上相關文章後,也是似懂非懂的,ㄧ直到閱讀”Metaprogramming Ruby” 後,才有了較深刻的了解。但文章內如有錯誤或是有不完整的地方都歡迎各位大大不吝指教!
“A method like this one, which is specific to a single object, is called Singleton Method”― Dave Thomas ―
Singleton Method
Singleton method 是讓物件自己擁有專屬的方法,任何其它物件或是類別都無法使用。但在開始介紹 singleton method 之前,先回顧昨天所提到的 “查詢方法流程 (method lookup)”。
class Animal
def run
@str = "running"
end
end
class Cat < Animal
def speak
"meow"
end
end
kitty = Cat.new
kitty.run # => "running"
當 run() 方法被呼叫時, kitty 物件(receiver) 向右尋找 Cat 類別,如果沒有找到再繼續往上層找,直到 run() 方法被找到為止。
接者讓 Ruby 在 kitty 物件上定義一個新方法 scratch(), 如下面程式碼:
kitty = Cat.new
lucy = Cat.new
def kitty.scratch
"scratching"
end
kitty.scratch # => "scratching"
kitty.singleton_methods # => [:scratch]
lucy.scrach # => (NoMethodError)
如此一來,scratch() 就只能被 kitty 物件所使用,就算再建立新的實例物件 lucy 呼叫 scratch(),也只能得到錯誤訊息 (NoMethodError)。這個 scratch() 方法就稱為 singleton method。 我們知道『實例變數是住在實例物件裡面,而方法是住在類別 』,那 singleton method 是住在哪呢?
- 選擇 A : 在 kitty 物件上? No,因為 kitty 物件不是類別
- 選擇 B : 在 Cat 類別上 ? No, 因為如果是在 Cat 類別上,所有的實例物件(包括 lucy 物件)就都可使用 scartch()
- 選擇 C :在 Singleton class 上 ? YES.
Singleton method 的所在位置並不是那麼明顯是有原因的,其實是在 Ruby 的世界裡,Object (物件) 是可以擁有自己的隱藏類別。這個隱藏類別就是 『 Singleton class 』,雖然也有其他的稱號,例如:anonymous class、eigenclass、metaclass 等…,但在 Ruby 原始碼上是稱為 singleton class。
加上 scratch()方法後,再看看查詢方法流程圖就應該如下圖:
Accessing Singleton Class
要直接存取 singleton class 可用以下方法:
class << an_object # an_object是你要存取的Object
# 放你的程式碼
end
# Create singleton method at instance object level
kitty = Cat.new
class << kitty
def scratch
"scratching"
end
end
kitty.scratch # => "scratching"
kitty.singleton_methods # => [:scratch]
# Create singleton method at Class level
class << Cat
def scratch
"scratching"
end
end
Cat.scratch # => "scratching"
Cat.singleton_methods # => [:scratch]
最後可以到 rails console 裡看看,其實 rails 大量地使用 Singleton methods。
“Class methods are a special kind of Singleton Method - and just as baffling”― Metaprogramming Ruby ―
The Truth About Class Method
定義類別方法時需在方法前加上 self。
class Cat < Animal
# instance method for instance object
def speak
"meow"
end
# class method is also a singleton method
def self.jump
"jumping"
end
end
p Cat.singleton_methods # => [:jump]
在上面程式碼中,可從 Cat.singleton_methods 證實類別方法也是 singleton method。
Singleton Classes and Inheritance
有了對 Singleton classes 的基本認識後,再來聊聊 Singleton classes 、classes 與 superclasses 之間的關聯。 以下面的程式碼為例子:
class Animal
# instance method for every instance object of Animal
def run
@str = "running"
end
end
class Cat < Animal
# instance method for every instance object of Cat
def speak
"meow"
end
end
# Add a class method/singleton method to Animal class
class Animal
class << self
def a_class_method
"C.a_class_method"
end
end
end
kitty = Cat.new
# Add a singleton method to kitty object
class << kitty
def scratch
"scratching"
end
end
箭頭 C 指向的物件並不等於類別方法(class method)住的地方,因為類別方法並不知道 singleton class 的存在。舉例來說:如果你問 kitty 的類別是什麼? Ruby 是會回傳 Cat ,但是其實 kitty 的類別應該是 kitty 的 singleton class, aka “ #kitty “ 。
Cat.superclass # => #<Class:Animal>
Animal.singleton_class # => #<Class:Animal>
Cat.singleton_class # => #<Class:Cat>
Cat.singleton_class.superclass # => #<Class:Animal>
Animal.singleton_class.superclass # => #<Class:Object>
( # 代表是 singleton class, #Cat 就是 singleton class of Cat)
可從上圖得知: #Cat 的 superclass 是 #Animal,同時也是 Animal 的 singleton class。 同樣地,#Animal 的 superclass 就是 #Object。 因此我們可以這樣說:
” The superclass of the singleton class is the singleton class of the superclass”― Metaprogramming Ruby ―
昏頭了嗎?其實多看幾次就會懂了!那也許有人會問:為什麼 Ruby 要把 class、superclass 及 singleton class 之間的關聯設計的如此令人困惑呢?答案是因為這樣的設計讓我們可以在子層(subclass)就可以呼叫到父層的類別方法。
Cat.a_class_method # => "Animal.a_class_method"
也許在 Cat 類別呼叫 Animal 類別所擁有的方法是再正常不過的事情了,不就是繼承所得到的方法嗎?
但是現在終於了解 Ruby 背後運作的幕後工程。
Comments