本章介紹四種核心的 DI 模式,並提供決策樹幫助開發者在不同情境下選擇正確的注入方式。
Composition Root#
Composition Root 是應用程式中組裝所有模組的唯一邏輯位置,盡可能靠近應用程式的進入點 (entry point)。
- 它不是一個方法或一個類別,而是一個概念——可能跨越多個類別與方法
- 每個應用程式應有且僅有一個 Composition Root
- 如果使用 DI Container,Composition Root 是唯一可以參照 Container 的地方

Figure 4.1: Composition Root 負責組裝鬆耦合類別的物件圖,直接依賴系統中所有模組
在 Composition Root 以外的地方使用 DI Container,就變成了 Service Locator 反模式(詳見第五章)。
在 Composition Root 中看到大量的依賴組裝程式碼是完全正常的,這種「Apparent Dependency Explosion」只是將原本分散隱藏在各處的耦合關係集中顯現,屬於基礎設施層級的關注點。

Figure 4.3: Mary 應用程式與鬆耦合應用程式的依賴圖比較
Constructor Injection#
Constructor Injection 是預設首選的注入方式,用於靜態宣告類別的必要依賴 (Required Dependencies)。
實作要點:
- 透過 constructor 參數接收 Dependencies
- 儲存於
private readonly欄位 - 使用 Guard Clauses 防止
null值
public class ProductService
{
private readonly IProductRepository repository;
private readonly IUserContext userContext;
public ProductService(
IProductRepository repository,
IUserContext userContext)
{
this.repository = repository
?? throw new ArgumentNullException(nameof(repository));
this.userContext = userContext
?? throw new ArgumentNullException(nameof(userContext));
}
}
Figure 4.5: 使用 Constructor Injection 建構 HomeController 實例
Constructor Injection 保證物件在建構完成後永遠不會處於不一致的狀態,因為所有必要依賴在建構時就已確認存在。
Method Injection#
Method Injection 適用於以下兩種情境:
- Dependency 隨每次呼叫而變化——呼叫端決定提供哪個實作
- 需要將 Dependency 注入以資料為中心的物件 (data-centric objects)——如 Domain Entity
public decimal ApplyDiscountFor(IUserContext userContext)
{
// 根據使用者身份計算折扣
bool preferred = userContext.IsInRole(Role.PreferredCustomer);
return preferred ? this.Price * 0.95m : this.Price;
}Method Injection 將依賴的選擇權交給呼叫端,而非在建構時就固定下來。
Property Injection#
Property Injection 的使用條件最為嚴格,僅適用於同時滿足以下兩個條件的情境:
- 存在一個良好的 Local Default(預設實作位於同一個 assembly,保證一定可用)
- Dependency 是真正可選的,使用預設值就能正常運作
Property Injection 不應該被用來替代 Constructor Injection。常見的錯誤是為了避免 Constructor Over-injection 而改用 Property Injection,這只是隱藏了問題(詳見第六章)。
主要使用場景是可重用函式庫 (reusable libraries) 中的擴充點 (extensibility model),讓使用者可以選擇性地替換預設行為。
決策樹#
選擇注入方式時,依序考慮:
- Dependency 是否為必要的? → 使用 Constructor Injection(大多數情況)
- Dependency 是否隨每次呼叫而變化? → 使用 Method Injection
- 是否有良好的 Local Default 且 Dependency 真正可選? → 使用 Property Injection

Figure 4.9: 模式決策流程——大多數情況應選擇 Constructor Injection
各模式的適用場景對照
| 模式 | 適用時機 | 典型範例 |
|---|---|---|
| Composition Root | 應用程式啟動時組裝所有模組 | Startup.cs、Main method |
| Constructor Injection | 宣告必要依賴(預設選擇) | Service 類別的 Repository 依賴 |
| Method Injection | 依賴隨呼叫變化或注入 Entity | ApplyDiscountFor(IUserContext) |
| Property Injection | 有 Local Default 的可選依賴 | 框架的 Logger、Formatter 擴充點 |