Singleton Class 與物件導向 <> 記錄指針不見了!我們該如何前進到下個島嶼

[Day29] 記錄指針是海賊王世界裡一種外觀像手錶,中央為球形的特殊羅盤。可以偵測並記下偉大航路內各個島嶼放出的磁場,為海上航行的船隻提供正確的路線。

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

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

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

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


Singleton Class 是什麼?

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

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

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

回顧昨天的例子:

1
2
3
4
5
6
7
8
9
10
11
class Cat
# ...
end

meme = Cat.new

def meme.feet
puts "有穿白襪"
end

meme.feet # => 有穿白襪

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

1
2
3
4
5
6
7
8
9
10
11
12
13
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,不過,通過類別物件的繼承或許可以看出一點端倪。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
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 裡物件導向的概念。

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

1
2
3
4
5
6
7
8
class Cat
# ...
end

meme = Cat.new

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

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

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

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

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

我們來對 Class 試試看吧:

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

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

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

做一個模組出來試試看:

1
2
3
4
5
module Flyable
end

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

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

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

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

接著我們來看看這個 Object

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

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

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

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

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

最後來看 Module

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

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

1
2
3
Object.class            # => Class
Class.superclass # => Module
Module.superclass # => Object

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

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


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

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

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


參考來源
Metaprogramming in Ruby 2 - The Object Model
speedred - Ruby 的繼承鍊 (1) - 如何實踐物件導向