鴨子型別 <> 終於抵達阿拉巴斯坦王國!迎接我們的是 · · · 卡魯鴨?

[Day26] 卡魯鴨:我跑遍了沙漠,好渴啊!

自從學了 Ruby ,就常聽到別人在說 **鴨子型別(Duck Type)**,可是卻一直沒有弄得很懂,今天決定來了解什麼是鴨子型別!

開始囉!


型別

型別,是一種為了讓資源(位元組)更有效率地被利用而規劃的概念。簡單來說,每筆資料都有自己所屬的型別,譬如:整數、字串、陣列等等...

鐵人賽第3天裡,我列出了一些 Ruby 常見的資料型別,各自做了簡單的介紹。但型別遠沒有這麼簡單啊!

Ruby 常被稱作是一種強型別的語言,然而,有強就有弱,判定標準在於語言能否自動轉換型態:像是 JavaScript,經常會偷偷地把變數轉換成可以被執行的資料型態(讓我覺得十分困擾)不過到了 Ruby 這裡,就會被仔細地檢查型別,型別不符的話則會發生錯誤。

還記得用怎麼類別產生實體物件嗎?產生實體物件也是一種將某個型別實體化的過程。

1
2
3
4
5
String.new("卡魯鴨")
=> "卡魯鴨"

String.new(123)
TypeError (no implicit conversion of Integer into String)

動態型別?

接著,我們可能還要了解「動態型別」是什麼。

Ruby 被認為一種 動態型別(Dynamically Typed) 的語言,「動態、不固定」的意思是 在命名變數時,不用先宣告是哪種型別

有了動態型別,我們就能寫出較為簡潔的程式碼。同時,也因為不用先提供型態資訊,因此變數型態是可以改變的。

舉個例子,比方說先把字串"卡魯鴨" 指定給區域變數 a

1
2
3
4
5
6
7
a = String.new("卡魯鴨")
=> "卡魯鴨"

a = 123

a
=> 123

變數 a 的型別被換成整數(Integer)了!


鴨子?型別?

終於要切入今天重點了!

鴨子型別是一種程式設計的風格。這種風格認為,一個物件的型別不是由這個物件所屬的類別決定,而是由「它能做什麼」來決定。

如果你看到一隻鳥走起來像鴨子,游泳起來像鴨子;叫起來也像鴨子;那麼這隻鳥就可以被稱為鴨子。

換句話說,想知道一個物件是什麼型別,看它能使用什麼方法就好。

接下來用程式碼來解釋,現在有 CatDog 兩個類別,彼此沒有相互繼承關係。

1
2
3
4
5
6
7
8
9
10
11
class Cat
def hungry # 同樣名稱的方法
puts "喵~肚子餓了!"
end
end

class Dog
def hungry # 同樣名稱的方法
puts "汪!肚子餓了!"
end
end

再建立一個 Pet 類別,同時定義一個 who_is_hungry 方法可以傳入 name,並讓 name 呼叫 hungry 執行:

1
2
3
4
5
6
7
8
class Pet
def initialize
end

def who_is_hungry(name)
name.hungry
end
end

接著做出幾個實體:

1
2
3
miru = Cat.new
wolf = Dog.new
pet = Pet.new

現在讓 miruwolf 當作帶入的參數試試:

1
2
3
4
5
6
pet.who_is_hungry(miru)
pet.who_is_hungry(wolf)

# 印出
喵~肚子餓了!
汪!肚子餓了!

用其他參數或另外的實體試試:

1
2
3
4
5
6
7
pet.who_is_hungry(123)
=>NoMethodError (undefined method `hungry' for 123:Integer)

fish = Pet.new

pet.who_is_hungry(fish)
=> NoMethodError (undefined method `hungry' for #<Pet:0x00007fd74407d0a0>)

可以看到 pet.who_is_hungry 在傳入 miruwolf 的當下,並沒有要求要檢查它們的型別,但是會確認它們都能執行 hungry 方法才能正常通過!

真是盡責的小鴨呢!


關於鴨子型別的的介紹就先到這邊啦!謝謝大家的觀看~