iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 27
0

老實說今天 Hackerrank 的主題讓我有點不想浪費一天的篇幅去講,因為是在講 Nested Logic,說穿了就只是要你練習兩層以上的 If Else 之類的東西,所以我相信應該不需要特別再去說明這個主題。不然今天就讓我們來聊聊先前一直想要探討的:為何在 Golang 或 Rust 會提倡 "以組合代替繼承" 吧!


繼承和組合的說明與比較

  • 繼承和組合,前者是 is a,後者則是 has a 的關係。一般來說,使用組合的比起繼承更加彈性以及不易有 Bug。但這並不表示繼承不好,而是說很多時候使用組合的方式可能會是更好的做法。當我們如果是使用繼承的話,必須要對父類別的實作去了解,這樣才知道我們需不需要在子類別去重寫,屬於 Whitebox Reuse。然而組合則是 Blackbox Reuse,表示不需要去了解 Object 的實作,而是把 Object 當作是 Blackbox 的方式去操作,只要我們替換了 Object 就能夠改變實際的行為方式。
  • 我們來舉個常見的例子,例如有個類型是汽車,汽車自然有很多種品牌,而每個品牌的車一定都有加速的功能,但加速的方式都不大一樣。那我們來看看繼承和組合這兩種不同的思維是如何來看這個問題。如果是繼承的話,首先我們直覺會想到先定義一個汽車類別 (Car),並定義 accelerate 這個方法,而不同品牌的車 (CarA, CarB ...)則分別宣告子類別並繼承 Car ,並將 accelerate 做 Override。但注意到在這裡每一種車的加速行為都是在編譯時期就已經決定而無法更改,例如說我們沒辦法將 CarB 的煞車行為在執行期時去做更換。接著讓我們看看如果是用組合的思維呢?首先我們先定義一個 Inrerface,其定義了加速的行為,例如 AccelerateBehavior,而不同的加速方式則是定義不同的類別去實作這個 Interface,例如 AccelerateAMethodAccelerateBMethod。接著有趣的來了,我們再也不用定義什麼 CarACarB,我們只要有一個類別 Car,而 Car 有個屬性 accelerateBehavior 類型是 AccelerateBehavior ,我們可以在創造 Car 的時候給予這個屬性不同的實作類別 (例如 AccelerateAMethodAccelerateBMethod),藉此將加速行為組裝進 Car 之中囉!因此根本來說,我們關注的是用繼承重寫還是給予不同的行為實例。
  • 繼承的優點是使用起來較為直覺,畢竟繼承是本來物件導向語言原生就有的做法,也很容易去做到程式碼的複用,只需要去 Override 需要更改的部分。但缺點是子類別跟父類別之間綁死,要是父類別改動了,子類別也必須改變。例如當很多子類別繼承時,要是今天父類別改變了行為,原先複用方法的子類別可能就必須要重寫,假使行為跟改動後的父類別已經不相同。另外就是子類別必須要了解到父類別的行為,所以也破壞了封裝性。最後就是如先前所說,行為在編譯時期的時候就已經決定。
  • 組合的優點是,由於組合是透過 Interface 引用,所以在複用的過程之中還能夠維持原先的封裝性。另外也如同上面所述,可以在執行期間輕易做替換,只要該類型實現了 Interface,就可以被拿來使用。而透過組合,可以讓類別的層次不會增長到一個無法管理的境界,讓層次維持很小,而每個實現 Interface 的類別都是專注在完成某個行為,更能夠維持內聚,也降低耦合。而缺點就是在程式碼的複用上比起繼承來說會比較需要多寫一些程式,以及涉及到的類別可能會比較多。

看看 Golang 怎麼做到組合

  • 由於繼承大家都已經很熟悉,也是物件導向語言的一大特性,所以我們就來看看 Golang 這個沒有原生繼承語法的語言,是怎麼樣用組合的方式來看世界吧!
package main

import (
    "fmt"
)

type AccelerateBehavior interface {
    Accelerate(speed int)
}

type AccelerateAMethod struct {
}

func (*AccelerateAMethod) Accelerate(speed int) {
    fmt.Println("Accelerate to " + string(speed) + "km by A")
}



上一篇
[Day 25] 與時間複雜度的競賽
下一篇
[Day 27] 自己的程式自己測
系列文
30 天把自己榨好榨滿的四週四語言大挑戰!30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言