高見龍

iOS app/Ruby/Rails Developer & Instructor, 喜愛非主流的新玩具 :)

Ruby 語法放大鏡之「類別跟模組有什麼不一樣?」

Ruby 語法放大鏡」系列短文主要是針對在大家學習 Ruby 或 Rails 時看到一些神奇但不知道用途的語法介紹,希望可以藉由這一系列的短文幫大家更容易的了解到底 Ruby 或 Rails 是怎麼回事。

在網路上常看到一些介紹 Ruby 裡類別(class)跟模組(module)的差別,有人說他們兩個差別很大,是完全不同的東西。事實上,類別跟模組是很像的。可能很多人不知道,類別跟模組是有血緣關係的,如果你試著這樣做:

1
puts Class.superclass

你會得到 Module 這個類別。也就是說,在 Ruby 裡,「類別」其實就是「模組」的後代,類別根本就是繼承自模組來的。即然是同一個體系,自然沒有太多的差異。

那差別是什麼?

最明顯的差別,當然就是一個是用 class 來定義,一個是用 module 來定義。至於前、後代功能上的差別,我們只要寫幾行簡單的程式就可以看得出來:

1
2
3
4
5
# 前、後代的類別方法差異:
p Class.methods - Module.methods

# 或直接這樣寫:
p Class.methods(false)

上面這段會得到 [] (空陣列),表示他們在類別方法上並沒有差別。再來看看實體方法的差別:

1
2
3
4
5
# 前、後代的實體方法差異:
p Class.instance_methods - Module.instance_methods

# 或直接這樣寫:
p Class.instance_methods(false)

會得到以下結果:

1
[:allocate, :new, :superclass]

其中,allocatenew 跟產生實體(instance)有關,superclass 跟繼承有關。

也就是說,類別比模組多了可以「產生實體」的能力以及「繼承」的能力。

那用途上有什麼差別?

請大家先思考一個問題:「如果我有一個小貓類別,我希望這個小貓會飛,你會怎麼做?」

  1. 直接寫一個有飛行功能的小鳥類別,然後再叫小貓類別去繼承它?

  2. 直接把飛行功能寫在小貓類別裡?

第 1 種做法的設計有點怪怪的,好好的貓不當,為什麼要去當鳥? 為了想要有飛行功能就去當別人家的小孩…

第 2 種做法看來似乎可行,但如果之後又有個「我希望我的這個小狗類別也會飛!」的需求,那這樣又得在小狗類別裡寫一段飛行功能,程式碼沒辦法共同。

這時候,模組就可以派上用場了。

1
2
3
4
5
6
7
8
9
10
11
12
module Flyable
  def fly
    puts "I can fly!"
  end
end

class Cat
  include Flyable
end

kitty = Cat.new
kitty.fly        # => I can fly!

在上面這段範例中,我做了一個飛行模組(Flyable),然後小貓類別不用特別寫什麼功能,就只要把這個飛行模組掛上去(include)就搞定了。

如果之後小狗類別也想要會飛的話,只要這樣:

1
2
3
class Dog
  include Flyable
end

小狗也會飛了。

要用繼承還是要用模組?

基本上,如果你發現你要做的這個功能,它可能在很多不同體系的類別裡都會用得到,那你可以考慮把功能包在模組裡,然後在必要的時候再 include 進來即可。

如果你還是不知道到底類別跟模組有什麼差別,我再舉二個例子。

不知道大家有沒看過火影忍者這部漫畫,漫畫裡的主人公之一,宇智波佐助,因為他們家族血統的關係,他寫輪眼這個功能是天生就有的,這個功能是從他的家族「繼承」來的。而佐助的老師,旗木卡卡西,他雖然也有寫輪眼功能,但他的寫輪眼並非繼承來的,事實上是他在年輕時候 include 了某個寫輪眼模組,所以才有這個效果。

另一個例子,海賊王漫畫裡,魯夫本來是普通人,但在偶然的機會下,他 include 了橡膠果實之後,他就有了橡膠人的能力了,並不是因為他老爸是橡膠人所以他才是橡膠人。

以上,希望能讓大家在使用類別跟模組時有更進一步的認識 :)

Comments