---
# System prepended metadata

title: Dependency Injection

---

# 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();
    }
}
```

### 對比總結
* 傳統寫法（高耦合）：
廚師自己決定去哪裡拿食材，固定只能煮牛肉麵，需求改變時會很麻煩。
* 依賴注入（低耦合）：
廚師不用管食材從哪裡來（市場還是超市），由經理負責準備好適合的食材，廚師專注於煮菜即可。這樣，無論顧客想吃牛肉麵還是拉麵，系統都能靈活應對。