iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 13
0
自我挑戰組

滿滿的紅寶石不拿嗎?-- 去吧!我把世界上的一切都放在那裡了! 系列 第 13

模組 <> 想得到夢寐以求的惡魔果實能力嗎?先裝一個吧! - 滿滿的紅寶石不拿嗎?

  • 分享至 

  • xImage
  •  

https://ithelp.ithome.com.tw/upload/images/20200921/20128363w0Jr8PDGKz.jpg

[Day12] 啊你說這個不能吃是嗎?


哈囉!今天要跟大家介紹的是模組(Module)

在 Ruby 的世界裡,模組是一個特殊又好用的東西,它就跟大名鼎鼎「惡魔果實」一樣,只要吃下去就能獲得那顆果實的特殊能力(完全是個不用靠爸就能成為主角的神奇道具啊!)

在理解模組時,可以想成是它加裝了一個東西原本沒有的功能,大概就像這樣:

這真是太神奇了傑克!

有了模組,我們就不需要使用昨天介紹到的類別繼承,那麼,要如何做到這件事呢?


include

請先詳閱以下使用說明書:

先定義一個模組 Floatable
模組的命名規則就和類別一樣,也必須是大字英文字母開頭的常數。

module Floatable
  def float
    puts "浮起來了!"
  end
end

再定義一個 Bicycle 的類別:

class Bicycle
end

產生一個 your_bike 實體,然後使用 float 方法試試看:

your_bike = Bicycle.new
your_bike.float

=> NoMethodError (undefined method `float' for #<Bicycle:0x00007fd90901ba28>)

居然失敗了!為什麼?
因為沒有在腳踏車上裝上可以浮起來的氣墊啊!怎麼可能會成功!

--- 需要在類別裡使用 include 來裝模組!---

Bicycle 類別裝上剛剛做好的 Floatable 模組吧!

class Bicycle
  include Floatable # 引入模組
end

重新產生一個 my_bike 實體,然後使用 float 方法:

my_bike = Bicycle.new
my_bike.float

# 印出
浮起來了!

嘿嘿~現在我的腳踏車可以浮在水面上了!


extend

接著,我們再來看看更神奇的 extend

假如你今天有一顆引擎的模組,然後把它裝在腳踏車上,腳踏車從此以後就可以用引擎運轉前進了!不過,如果想把腳踏車改造成氮氣噴射的功能,變成阿姆斯特朗旋風噴射阿姆斯特朗砲 的話呢?

這時候就會用上讓模組擴充模組的 extend 了!
廢話說完了,接著來看 code:

module Engine
  def run
    puts "全速啟動!"
  end
end

module Acceleration
  def accelerate
    puts "氮氣噴射!!!"
  end
end

class Bike
  include Engine
  extend Acceleration
end

max = Bike.new

max.run

# 印出
全速啟動!

Bike.accelerate
# 印出
氮氣噴射!!!

看出來了嗎? 原本的 Engine 模組在被 include 後,只是讓腳踏車可以用引擎運轉,但 Acceleration 模組被 extend 後則徹底改造了腳踏車這個類別,讓它完全升級為一種不同的交通工具了(誤)

但如果讓 max 呼叫 accelerate 方法或是讓 Bike 呼叫 run

max.accelerate
# 印出
NoMethodError (undefined method `accelerate' for #<Bike:0x00007fec3f2aaf70>)

Bike.run
# 印出
NoMethodError (undefined method `run' for Bike:Class)

就都找不到方法了,所以...

說人話版本 =>

Bike.accelerate  # 類別方法 / 用 extend 引入
Bike.new.run     # 實體方法 / 用 include 引入

明天會再介紹什麼是類別方法和實體方法!(挖坑)


聽說模組和類別很像?

是的,他們很像。
譬如名字都是大寫字母開頭、裡面會放一些方法。在專案裡,如果只看到一個大寫開頭的常數,並沒有辦法確定現在看到的是模組還是類別!不過,它們還是有一些明顯的差異。

舉個例子,可能很多人在純真的童年時會夢想像鳥類一樣在天上飛,在20世紀初,萊特兄弟發明了可以載人的飛機,但直到今天,人們仍然不算是學會了飛行。

如果真的要讓人類可以飛,該怎麼做?

首先,鳥類這個類別應該會有一個 fly 的方法,

class Bird
  def fly
    # 可以飛行的秘密
  end
end

然後繼承它?
class Human < Bird
end
等等!我們的祖先又不是鳥類,所以不應該使用類別繼承吧!

那掛載一個會可以讓人飛行的模組?

module Flyable
  def fly
    # 可以飛行的秘密
  end
end

class Human
  intend Flayable
end

我們終於成功裝上了 Flyable !從此以後,人類就可以飛了!


ClassModule 的方法數量

瞎扯了一堆,接著我們直接進程式裡看看:

# 查詢 Class 和 Module 的方法和數量

p Module.instance_methods.count
109

p Class.instance_methods.count
112

instance_methods.count 可以幫我們計算一個類別能使用的方法有多少,可以發現雖然兩者的方法都很多,但 ClassModule 硬是多出了 3 個方法!

來看看多出的是哪幾個:

p Class.instance_methods(false)
[:new, :allocate, :superclass]

由於 Module 少了 :new, :allocate, :superclass 這些方法 ,所以:

  1. 模組沒辦法 new 一個新的實體。
  2. 模組也 無法繼承 別的模組。
m = Module.new

m
=> #<Module:0x00007f97268b6310> 
m.new

=> NoMethodError (undefined method `new' for #<Module:0x00007f97268b6310>)

Namespace::

namespace?這又是什麼?想像一下,我的車有一顆引擎,你的車也有一顆引擎,但你開的是 BMW 旗艦 V12 雙門跑車,我開的是 Toyota Camry (Camry 錯了嗎?),那這兩輛車會是同樣的車嗎?很明顯不是!(怒)

那要怎麼解決這種問題呢,簡單來說,namespace 就是為了避免叫到同名類別,導致程式無法判讀的問題。我們在呼叫類別時,可以連名帶姓地呼叫:

譬如專案裡常常會看到的:

# 類別(模組)::類別

class User < ActiveRecord::Base
end

class A
  module B
  end
end

如此一來,我們就能很清楚地知道 User 類別是繼承自 Base 類別,而這個 Base 類別是屬於 ActiveRecord 這個模組(或類別)裡的,其他地方如果有叫 Base 的類別都不是!

有興趣的朋友可以看看龍哥的這篇大作:
Ruby 語法放大鏡之「有時候會看到有兩個冒號寫法是什麼意思?」


說了這麼多,今天就先到這邊囉!
相信大家應該都認識模組了!明天見囉!

https://ithelp.ithome.com.tw/upload/images/20200921/20128363XrK019kUJO.jpg
旗木卡卡西:是不是忘了介紹我?寫輪眼才是最強的模組啊!


上一篇
繼承 <> D 的意志!鐵拳卡普、革命家龍、草帽魯夫,這一家真的沒開外掛嗎? - 滿滿的紅寶石不拿嗎?
下一篇
類別方法 <> 雜魚們!為革命軍獻出你們的心臟吧! - 滿滿的紅寶石不拿嗎?
系列文
滿滿的紅寶石不拿嗎?-- 去吧!我把世界上的一切都放在那裡了! 30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言