什麼是依賴反轉?
💡
本文涵蓋多個技術細節,但關鍵重點是「依賴反轉就是依賴抽象」。
「業務邏輯」和「實作細節」兩端都依賴抽象
業務邏輯 ➡️ 抽象介面 ⬅️ 實作細節
用手作甜點店比喻依賴關係
舉例來說:你經營一家手作甜點店 (業務邏輯/Domain層):
需要食材來製作甜點
但你不親自去買
告訴採購部需求(Repository介面)
採購部會:
選擇供應商可能是批發市場、農場直送、進口等
買到食材(實作Repository)
這樣設計的好處
你可以專注在做甜點,你不用管食材怎麼買 →業務邏輯 依賴抽象 ⬇️
告訴採購部需求→Repository介面
採購部選擇供應商買食材→實作抽象Repository 依賴 ⬆️
原則
維基百科:https://zh.wikipedia.org/wiki/%E4%BE%9D%E8%B5%96%E5%8F%8D%E8%BD%AC%E5%8E%9F%E5%88%99
高層次的模組不應該依賴於低層次模組
高層次模組:抽象、業務規則、接近人思維
低層次模組: 實作細節、數據操作、接近電腦運作
依賴:處理怎麼做
目的:解耦高低層模組,低層細節變更時,不影響高層業務邏輯
抽象接口(介面)不應該依賴於具體實現,而具體實現應該依賴於抽象接口(介面)
- 意思是抽象接口不要有具體的實作細節
// ❌ 錯誤:介面包含市場A的具體實作細節
interface Purchase {
// 依賴特定市場的營業時間
fun buyBeforeMarketAClose()
// 依賴特定市場的付款方式
fun payWithMarketACreditCard()
// 依賴特定市場的食材
fun getMarketAIngredients(): MarketA
}
// ✅ 正確:介面只定義要做什麼,不管是從哪裡買
interface Purchase {
// 純抽象的採購行為
fun buy()
// 使用通用的食材型別
fun getIngredients(): List<Ingredient>
}
抽象:專注在做什麼而非
怎麼做,目的是隱藏實作細節。- 實現方式:介面 (Interface)、抽象類別 (Abstract Class)
目的:透過接口,允許不同的具體實作輕鬆替換。
依賴說明
依賴=直接處理怎麼做
實作程式細節
你親自去採購
依賴反轉=關注要做什麼
透過介面告知需求
只要告訴採購部需求
為什麼要依賴反轉?
有無使用依賴反轉的差異
比喻 | 甜點師 ➡️ 超市A(實作程式) | 甜點師 ➡️ 採購部(介面) ⬅️ 超市A/批發商B/農場C |
依賴狀態 | 沒有依賴反轉 | 有依賴反轉 |
特點 | 綁定特定實作、更換供應商需改動業務邏輯 | 透過介面解耦、實作可自由替換 |
程式碼示範:
// 沒有依賴反轉:
class ChefService {
// 依賴特定超市
private val marketA = MarketA()
fun getIngredients() {
marketA.buy() // 綁定實作
}
}
// ------------------------------------
// 有依賴反轉:
interface Purchase { // 採購部介面
fun buy()
}
class ChefService {
// 依賴介面
private val purchase: Purchase
fun getIngredients() {
purchase.buy() // 透過介面呼叫
}
}
// 不同供應商實作介面
class MarketA : Purchase {
override fun buy() { /* 實作 */ }
}
class MarketB : Purchase {
override fun buy() { /* 實作 */ }
}
class Farm : Purchase {
override fun buy() { /* 實作 */ }
}
雖然我們實現了依賴反轉,但這樣的程式碼還是無法執行,因為 purchase
沒有被初始化。需要透過依賴注入(DI)來實現。
class ChefService(
private val purchase: Purchase // 透過建構子注入
) {
fun getIngredients() {
purchase.buy()
}
}
總結
💡 依賴反轉概念:
業務邏輯不應依賴實作細節
兩者應該依賴抽象 (介面)
✨ 優點:
業務邏輯穩定性高
- 實作方式改變時,核心邏輯不需修改
程式靈活
- 可替換不同實作(如:更換供應商)
方便測試
- 可替換測試用的模擬實作
🔑 判斷重點:
實作 = 依賴
介面 = 依賴反轉
💡
本文涵蓋多個技術細節,但關鍵重點是「依賴反轉就是依賴抽象」。