依賴注入新手指南:白話解釋依賴注入、控制反轉與依賴反轉原則背後的核心思想
前情提要:
我剛開始學 DI、IoC 和 DIP 的時候,查了不少文章,但大多一上來就塞滿了什麼「依賴啦」、「耦合啦」、「抽象啦」,還有「高低階模組」、「容器」、「注入」這些 OOP 的專業詞彙。對於剛接觸 OOP 或對 OOP 不太熟悉的人(比如我)來說著實不太友善。連這些詞彙是什麼意思都還沒搞清楚,就要用它們來理解 DI、IoC 和 DIP,實在有點太難了。本篇文章的目標是讓對 OOP 與 Design Pattern 不熟悉的人也能對以上三個觀念建立清楚的理解。因此,在這篇文章的開頭,我想要用 最簡單易懂的方式帶大家先建立對 DI、IoC 和 DIP 的基本概念,讓大家掌握它們背後的核心思想,而不被繁雜的專業術語困擾。接下來的章節中,我們會逐步深入,探討這些術語和技術細節,以便完整理解這些設計原則。
快速認識依賴注入 (DI)、控制反轉 (IoC)、依賴反轉原則 (DIP)
S.O.L.I.D 軟體架構中的 D 所代表的就是依賴反轉原則 (Dependency Inversion Principle, DIP) ,在物件導向設計 (OOD) 中是個非常重要的設計原則。這個原則與我們經常提到的控制反轉 (Inversion of Control, IoC) 和 依賴注入 (Dependency Injection, DI) 緊密相關,這些概念共同構成了一個強大且靈活的架構基礎。
-
DIP - Dependency Inversion Principle (依賴反轉原則)
想像你是一位經營餐廳的主廚,而食材的供應非常重要。如果你依賴某一家固定的農場供應食材,那麼如果這個農場關閉或供應中斷,你的餐廳運營就會受到影響。依賴反轉原則告訴你,不要依賴某一家具體的農場,而是應該依賴於“供應商的概念”。也就是說,你應該和一個供應系統合作,這樣無論哪家供應商提供食材,你都能正常運營。
DIP 是一種軟體架構的設計原則與思想,強調高階模組不應該依賴具體的低階模組,而是依賴抽象的接口或契約。這樣可以輕鬆替換具體的實作,系統的可擴 展性和穩定性也會更好。
-
IoC - Inversion of Control (控制反轉)
接著看控制反轉,想像如果你是一個廚師,傳統上你可能自己去菜市場選購食材,然後每天回來準備料理。但控制反轉的概念告訴你,讓“供應商”替你負責這件事。他們知道你每天需要什麼,會自動送來。這樣,你的控制權不再在你手中,而是交給了外部的供應商,讓他們負責供應流程。你只需要專心做菜。
IoC 是一種 DIP 的具體實踐方式,它的核心在於,將控制物件的創建和依賴的管理交給外部容器或框架,讓框架來管理這些過程,讓程式模組更專注於各自的功能。
-
DI - Dependency Injection (依賴注入)
最後是依賴注入,它是控制反轉的一種具體實現方式。假設你每天煮飯時,食材供應商會把所有食材直接送到你的廚房。你不需要去尋找或挑選供應商,供應商會自動送來食材。這就是依賴注入,供應商在你需要時自動提供所需的資源(食材),讓你專心做自己的事。
DI 則是 IoC 的一種具體實現方式,它的重點在於,當一個模組需要某些依賴(例如服務或資源)由外部注入,而不是自己去建立。這樣可以讓程式更加模組化,易於測試和維護。
OOP 中的核心概念
依賴
「依賴」正是本篇文章要討論的主角,在我們開始討論依賴反轉、依賴注入之前,首先讓我們理解一下他的含義。
在 OOP 中,依賴是指一個物件需要外部的另一個物件或服務來完成某些功能。舉例來說:假設你經營一家咖啡店,你需要一台咖啡機來煮咖啡。你的咖啡店沒有咖啡機就無法運作,這時候可以說咖啡店依賴咖啡機來運行。
// 定義 CoffeeMachine 類別
class CoffeeMachine {
makeCoffee(): string {
return 'Here is your coffee!';
}
}
// CoffeeShop 類別依賴於 CoffeeMachine
class CoffeeShop {
private coffeeMachine: CoffeeMachine;
constructor() {
this.coffeeMachine = new CoffeeMachine();
}
serveCustomer(): void {
const coffee = this.coffeeMachine.makeCoffee();
console.log(coffee);
}
}
耦合
所謂的耦合是指兩個或多個物件、模組或系統之間的依賴程度。當兩個系統或物件 之間高度依賴時,一個物件或系統的變動會嚴重影響另一個物件或系統的運作,這就表示它們的耦合度很高。
這裡我們一樣舉個通俗易懂的例子:假設你有兩家合作的餐廳:一家專門供應食材(供應商),另一家專門烹飪食物(餐廳)。如果這家餐廳只能使用特定一家供應商的食材(高度耦合),那麼當供應商無法供應時,餐廳也會無法運作。如果餐廳能靈活地選擇不同的供應商(低耦合),即使一家供應商無法提供食材,餐廳依然可以正常運營。
當系統的耦合度過高時,系統變得難以維護,因為一個小小的變動可能會對多個部分造成連鎖反應。而 DIP 的主要概念就是盡量降低模組間的耦合度,讓程式更具可擴展性。
耦合和依賴的關係可以這樣理解:依賴描述了物件之間的需求,耦合描述了這種需求的強弱。高耦合往往表示依賴關係比較強,而低耦合則意味著依賴關係比較鬆散。