Inversion of Control (IOC,控制反轉) 與 Dependency Injection (DI,依賴注入) 是現代軟體架構的基石,但兩者常被混淆。Martin Fowler 指出:「IOC 是框架的共同特徵,說輕量級容器特別是因為使用 IOC,就像說我的車特別是因為它有輪子。」本文將從生活類比、程式碼演進、容器視角三個維度,建立清晰的心智模型。
完整流程即為一次 **控制權轉移週期** —— 這就是 IOC 的本質:控制權在不同角色間流轉。
public class B { public void DoMethod() { /* ... */ } }
public class A
{
public void Execute()
{
B b = new B(); // A 直接實例化 B
b.DoMethod();
}
}
// 呼叫端
var a = new A();
a.Execute();
問題:A 與具體類別 B 緊耦合,B 變動 (建構子參數、介面變更) 直接衝擊 A,單元測試難以模擬 B。
public interface IB { void DoMethod(); }
public class B : IB { public void DoMethod() { /* ... */ } }
public class A
{
private readonly IB _b;
public A(IB b) { _b = b; } // 建構子注入
public void Execute() { _b.DoMethod(); }
}
A 不再依賴具體 B,而是依賴抽象 IB。但誰負責建立 B 並傳給 A?
// 容器設定 (以 Autofac 為例)
var builder = new ContainerBuilder();
builder.RegisterType<B>().As<IB>();
var container = builder.Build();
// 解析根物件,容器自動遞歸注入依賴鏈
var a = container.Resolve<A>();
a.Execute();
現在 控制權完全反轉給容器:容器掃描註冊、解析依賴圖、建立實例、管理生命週期 (Singleton/Transient/Scoped)、處理循環依賴。應用程式碼只專注業務邏輯,不再負責「new」。
| 模式 | 特徵 | 適用場景 |
|---|---|---|
| 建構子注入 | 強制依賴、不可變、易於單元測試 | 必要相依服務 (90%+ 情況首選) |
| 屬性注入 | 可選依賴、可變更、支援循環依賴 | 選用服務、外掛擴充點 |
| 方法/介面注入 | 執行時動態決定、較少見 | 策略模式、動態攔截 |
筆者觀點:介面注入本質上是建構子/屬性注入的特殊形式,不必單獨列為第三大類。
var service = Container.Resolve<IService>()。但這屬於「拉取」模式,隱藏依賴關係,難以測試,現已被視為反模式。若將應用程式比作樂團:
B, C, D...)IB, IC...)指揮家 (容器) 依據樂譜 (註冊資訊) 協調各樂器 (服務) 入場、演奏、退場。樂器不需知道旁邊是誰在演奏,只需專注發出正確的聲音。這就是 **高內聚、低耦合** 的具體體現。
IOC 是架構層面的 **設計原則** (控制權反轉);DI 是實現該原則的 **具體模式** (依賴注入);容器是落地 DI 的 **基礎設施**。理解三者關係,不再被名詞困擾,才能在專案中做出正確的架構決策:依賴抽象、注入具體、交給容器。