# Dependency Injection ### 1.什麼是依賴注入(DI)? 依賴注入是一種設計模式,用於實現類與類之間的鬆耦合。簡單來說,就是將一個物件所依賴的其他物件(如服務或資料庫存取層)從外部傳遞進來,而不是在類內部自己建立。 依賴注入是實現**控制反轉**(Inversion of Control, IoC)的一種方法。 --- ### 2.為什麼需要依賴注入? 傳統寫法中,類與類之間是直接依賴的,這會導致以下問題: * 耦合性高:類與具體實現綁定,難以更換實現或進行單元測試。 * 可測試性差:無法輕易替換依賴物件來進行測試(例如,無法使用 Mock 測試資料)。 * 可維護性差:當依賴的類變化時,必須修改所有相關的代碼。 DI 通過將依賴交由**外部管理**,解決了這些問題。 --- ### 3.DI 的好處 * 降低耦合:類與依賴的實現分離,只依賴於接口(Interface)。 * 提高可測試性:可以輕易使用 Mock 物件進行單元測試。 * 增強靈活性:可以輕鬆替換不同的依賴實現(如更換資料庫服務)。 * 提高可維護性:依賴管理統一,方便修改或擴展。 --- ### 4.DI 的壞處 * 學習曲線:對初學者來說,理解 DI 容器和配置可能需要時間。 * 初期開發成本:為了實現 DI,通常需要定義接口和使用額外的配置。 * 過度設計的風險:過於追求靈活性可能導致設計變得複雜,尤其是在小型項目中。 DI容器: Microsoft.Extensions.DependencyInjection 是最常見的 DI 容器,它幫助我們管理依賴的注入和生命週期(如 Singleton, Scoped, Transient)。 --- ### 5.餐廳點餐例子:用 C# 說明 ### 傳統寫法(高耦合) 想像你經營一間餐廳,廚師只能煮「牛肉麵」,因為他直接去市場買了牛肉和麵條,然後回來煮。 問題:如果有顧客想吃別的(例如拉麵),廚師就無法改變,因為他固定只能煮牛肉麵。 ``` public class Market // 市場 { public string GetIngredients() => "牛肉和麵條"; } public class Chef // 廚師 { private Market _market; public Chef() { _market = new Market(); // 廚師直接依賴市場提供的固定食材 } public void Cook() { string ingredients = _market.GetIngredients(); Console.WriteLine($"煮了一碗 {ingredients}"); } } // 主程式 class Program { static void Main(string[] args) { Chef chef = new Chef(); chef.Cook(); } } ``` #### 情境解釋: 廚師 (Chef) 直接依賴於市場 (Market)。 如果市場只賣牛肉和麵條,那餐廳永遠只能提供牛肉麵,顧客需求無法變化。 ### 使用依賴注入(低耦合) 現在,你決定讓經理來負責採購食材,而不是廚師自己去市場。 經理根據顧客需求(例如「拉麵」、「牛肉麵」)準備好食材,然後將食材交給廚師,廚師只專注於煮菜。 好處:**廚師不再依賴市場**,無論經理準備什麼食材,廚師都可以輕鬆應對不同的需求。 ``` public interface IIngredientProvider // 食材供應者 { string GetIngredients(); } public class BeefNoodleIngredients : IIngredientProvider // 牛肉麵食材 { public string GetIngredients() => "牛肉和麵條"; } public class RamenIngredients : IIngredientProvider // 拉麵食材 { public string GetIngredients() => "豬肉和拉麵條"; } public class Chef // 廚師 { private readonly IIngredientProvider _ingredientProvider; public Chef(IIngredientProvider ingredientProvider) { _ingredientProvider = ingredientProvider; // 食材由外部傳入 } public void Cook() { string ingredients = _ingredientProvider.GetIngredients(); Console.WriteLine($"煮了一碗 {ingredients}"); } } // 主程式 class Program { static void Main(string[] args) { // 顧客想要牛肉麵 IIngredientProvider beefNoodleIngredients = new BeefNoodleIngredients(); Chef chef1 = new Chef(beefNoodleIngredients); chef1.Cook(); // 顧客想要拉麵 IIngredientProvider ramenIngredients = new RamenIngredients(); Chef chef2 = new Chef(ramenIngredients); chef2.Cook(); } } ``` ### 對比總結 * 傳統寫法(高耦合): 廚師自己決定去哪裡拿食材,固定只能煮牛肉麵,需求改變時會很麻煩。 * 依賴注入(低耦合): 廚師不用管食材從哪裡來(市場還是超市),由經理負責準備好適合的食材,廚師專注於煮菜即可。這樣,無論顧客想吃牛肉麵還是拉麵,系統都能靈活應對。