什麼是依賴? 什麼是依賴反轉原則? (三)

Kash Yang
5 min readJun 26, 2023

--

前兩篇提到了依賴,以及抽象化,這一篇我們要來透過這些觀念,聊一聊依賴反轉原則,我們一樣先從定義開始

何謂依賴反轉原則

Wiki説:

高層次的模組不應該依賴於低層次的模組,兩者都應該依賴於抽象介面。

抽象介面不應該依賴於具體實現。而具體實現則應該依賴於抽象介面。

首先我們得先了解什麼是高層模組,什麼又是低層模組

低層模組:這個模組的實作已經不能再分割下去了,以先前的例子來說就是Macbook Pro

高層模組:這個模組的實作是由很多低層模組所組成,以先前的例子來說就是iOS Programmer

反轉又是什麼?就是低層模組變成反過來依賴於高層模組對於這個抽象的實作。(好文謅謅的一句話)

意思就是低層模組的實作,會被高層模組本身決定,是不是還是有點複雜?

沒關係,我們直接來用例子看一下,我們首先把先前的範例,按照依賴反轉原則來改寫一下。

怎麼改寫?直接看定義:使用抽象介面

我們只需要將上一篇的寫法,將mac抽象化之後改為注入 (inject)的方式,就能符合依賴反轉原則囉

//定義一個XcodeRunnable的interface
interface XcodeRunnable {
fun build()
}

class MacbookPro: XcodeRunnable {
override fun build() {
// build with Macbook Pro
}
}

class MacbookAir: XcodeRunnable {
override fun build() {
// build with Macbook Pro
}
}

class IOSProgrammer(private val mac: XcodeRunnable) {
fun buildCode() {
mac.build()
}
}

使用上就是

IOSProgrammer(MacbookPro()).buildCode() //配發新的Macbook Pro
IOSProgrammer(MacbookAir()).buildCode //配發新的Macbook Air
IOSProgrammer(oneOldMacbookPro).buildCode() //配發舊的Macbook Pro

至於Swift也只需要做相同的動作即可

struct IOSProgrammer {
var mac: XcodeRunnable
func buildCode() {
mac.build()
}
}

對比一下先前的程式碼

// 此為不符合反轉依賴原則的做法
class IOSProgrammer {
private val mac: XcodeRunnable = MacbookPro()
fun buildCode() {
mac.build()
}
}

兩者在使用上的差異為

// IOSProgrammer 一生成就決定了用哪一台機器
IOSProgrammer().buildCode()

// IOSProgrammer 與她依賴的mac物件,都依賴於此刻XcodeRunnable的實作,
// 也就是新的MacbookPro
IOSProgrammer(MacbookPro()).buildCode()

應該不難發現哪一種的做法彈性比較大,也看到決定抽象介面XcodeRunnable實作的人不再是Programmer,而是在生成的當下決定,所以

本來是Programmer依賴mac的狀況,變為這兩者都依賴於XcodeRunnable的實作,並且增加彈性,這就是依賴反轉原則

如此一來配發電腦的問題就解決了,同時你也了解了依賴反轉原則是什麼,以及該怎麼實作!

什麼!?講完了嗎?看完還是不太會用啊!

其實了解完定義 (名詞解釋) 之後就可以把他丟了,因為沒人在討論什麼誰是高層模組,誰是低層模組,實務面上誰依賴誰沒那麼重要。

重要的是模組間存在依賴關係的這件事情本身,而

依賴反轉原則就是要解決模組之間複雜的依賴關係而生的準則

而且不會用是正常的,因為沒有真的寫過。單純看範例跟自己寫還是有些差距,還是建議自己練習在專案中使用會學得比較快喔。

這個系列的三篇都沒看很懂也沒關係,教大家一個心法,先讓自己養成習慣,寫多了就會進步,那就是

只要你的模組有依賴關係 (使用到別的模組),不管一切先把依賴關係設計為interface並且使用注入 (inject)的做法再說

時間久了,你就會慢慢體會到抽象化以及依賴反轉原則所帶來的好處,並且會找到抽象化的平衡點,畢竟所有的抽象都得落實到實作。

祝各位coding愉快!

另外依賴反轉原則可以帶出很多觀念跟實戰技巧。

比如說透過練習你可能會發現,其實這個範例中的Programmer也能抽象化,畢竟公司不只有iOS工程師吧?可能有Android,還有前端或是後端工程師等等。

你也可能因為按照本篇範例要用注入Inject的方式來實作,卻發現多到爆的constructor參數,不知該怎麼辦,你可能會找到DI (Dependency Injection) 的相關資料。

或是因為要針對不同的case給出不同的實作而發現Design Pattern 中的工廠模式

再來如果你是iOS developer,你可能會發現protocol extension的強大之處。

這些觀念就讓各位自己去挖掘,期待未來有機會我們再挑幾個來講一講,但針對依賴反轉原則還有一個延伸主題也很重要。

下一篇我們來講一下依賴反轉的最直接好處:測試

--

--