Ruby Singleton Class 與物件導向

Andrew
7 min readDec 10, 2020

--

延續昨天的主題,我們已經知道單體方法是怎麼被定義的,但它既不屬於類別方法,也不是實體方法,那它到底被定義在哪裡呢?

先說結論:單體方法被寫在 Singleton Class 裡。

走過的路 是一陣魔術···
···都化作這目光 吟唱成一首歌

抱歉突然想唱個歌,讓我們進入正題~

Singleton Class 是什麼?

如果是初學 Ruby 這門程式語言的朋友,一開始幾乎不太會接觸到 Singleton Class 的概念。在一般的繼承鍊裡,我們只學過 Class < Object < BaseObject 這種一直往上找,層層相疊的繼承關係。

不過實際上在 Ruby 的物件導向設計裡,還有一個非常神祕的類別,
那就是 Singleton Class

  • 它是設計給單一物件使用的類別
  • 又被稱為 metaclasseigenclass
  • Singleton Class 上定義的方法就是單體方法

回顧昨天的例子:

class Cat
# ...
end
meme = Cat.newdef meme.feet
puts "有穿白襪"
end
meme.feet # => 有穿白襪

雖然單體方法 feet 是寫在類別的外面,但 Ruby 其實有偷偷為 meme 準備了一個 Singleton Class ,而 feet 方法就被定義在裡面:

class Cat
# ...
end
meme = Cat.new# singleton class of meme
def meme.feet
puts "有穿白襪"
end
# end
meme.feet # => 有穿白襪

有看到 Singleton Class 嗎?

沒看到?

嗯··· 代表你非常正常!

通常在開了天眼後才看得到

所以泰安老師叫它「跟鬼一樣的類別」!

我要在哪裡找到 Singleton Class

其實我還不知道如何在 irb 裡顯示物件的 Singleton Class,不過,通過類別物件的繼承或許可以看出一點端倪。

繼承最核心的概念就是子層類別的物件可以拿父層類別的方法來用,類別方法也同樣遵循著這套繼承的規則

class Animal
def self.all
puts "全部的動物"
end
end
class Cat < Animal
def self.all
puts "貓咪王國!"
end
end
Cat.all # => 貓咪王國!

如果有同名的類別方法,則會優先呼叫定義在 Singleton Class 的那個。

昨天有提到,類別方法其實也是單體方法的一種,所以每個類別的類別方法就會定義在自己的 Singleton Class 上,像是這樣:

現在我們知道了,當一個 Ruby 物件在呼叫方法時,會先往 Singlton Class 找看看,沒有的話,會再往上從繼承的類別裡尋找。

Ruby 裡的物件導向:

既然都已經講了 Singleton Class,那就不得不來介紹 Ruby 裡的物件導向了!

「物件導向」這四個字看起來又是個高大上,一開始總覺得如果能說清楚這個概念,好像自己會變很聰明,但事實上,它就只是個語言的設計概念。(另一個事實則是我笨)

接下來會用到這兩個方法:

方法說明superclass尋找自己繼承的父層類別class尋找自己所屬的類別

superclassclass 能有助於更加釐清 Singleton Class 以及 Ruby 裡物件導向的概念。

繼續沿用剛才的例子,並用這兩個方法來看看吧!

class Cat
# ...
end
meme = Cat.newmeme.class # => Cat
meme.superclass # => NoMethodError (undefined method `superclass' for #<Cat:0x00007fd482123eb0>)

咦? superclass 噴錯了!不過沒關係,因為實體物件本來就沒有繼承的父層類別,繼續來看:

Cat.class               # => Class 
Cat.superclass # => Object

Cat 這個類別是屬於一個叫做 Class 的類別,或者說,在 Ruby 裡,所有的類別都屬於 Class 這個類別。

Cat 這個類別是繼承自一個叫做 Object 的類別,"Object",也就是物件的意思,以一個物件導向的語言來說,出現這個名字很合理,繼續看下去:

我們來對 Class 試試看吧:

Class.class             # => Class 
Class.superclass # => Module

這次出現了有趣的事情,果然全天下的類別都是屬於 Class 這個類別,因此 Class 所屬的類別也是 Class 自己。

Class 繼承的類別居然是 Module ! 這不是模組嗎?不過仔細一瞧才發現大寫的 Module 是類別,小寫的 module 則是模組,只是,Ruby 設計者沒想過會讓大家困惑嗎XDDD

做一個模組出來試試看:

module Flyable
end
Flyable.class # => Module
Flyable.superclass # => NoMethodError (undefined method `superclass' for Flyable:Module)

Flyable 這個模組屬於 Module 這個類別,而 Flyable 沒有向上的繼承類別(這是因為 Ruby 裡的模組本來就無法繼承)

但令人驚訝的是 Class 竟然是繼承自 Module !當初在學習時,就有發現模組和類別兩個概念很像,但我原本一直以為模組可能是類別的副產品之類的,

沒想到···竟然是反過來
真是世事難料啊!!!

接著我們來看看這個 Object

Object.class            # => Class 
Object.superclass # => BasicObject

不出所料,Object 的類別是 Class,而繼承自 BasicObject 類別這個又是什麼?名字怎麼都取這麼像啊!

BasicObject.class       # => Class 
BasicObject.superclass # => nil

BasicObject 的類別也是 Class,而再往上就沒有了!

目前可以得到像這樣的圖:

最後來看 Module

Module.class            # => Class 
Module.superclass # => Object

嗯···類別是 Class,然後繼承自···咦!Object!那不就

Object.class            # => Class 
Class.superclass # => Module
Module.superclass # => Object

你們這樣繞來繞去,弄得我頭很痛啊!還好網路上已經有大大梳理好它們彼此之間的關係

看圖是不是清楚多了呢?
說實話,韓劇每個角色的關係圖都比這都複雜多了!

今天的文章就先到這裡了,

以上就是我對 Ruby 物件導向的理解,自己覺得這篇的脈絡有點亂,就和思緒一樣,可能也有一些理解錯誤的地方,大大們如果看到的話還請不吝指出,日後還會繼續進行釐清和整理的。

覺得好像感冒了…頭好昏啊!

--

--