在面向對象程式設計中,解耦是提升程式碼可讀性與可維護性的關鍵。控制反轉是實現低耦合的核心設計原則,而依賴注入則是控制反轉最常見、最實用的實現方式。簡單來說,依賴注入的核心思想是:類別不再自行建立其所需的依賴物件,而是由外部容器或建構者將依賴「注入」進來。
以汽車(Car)與動力源(Energy)為例:
interface Energy { }
class GasEnergy implements Energy { }
class Car {
Energy energy = new GasEnergy(); // 硬編碼依賴
}
這樣的設計存在三大弊端:
Car 類別負責建立具體動力物件,違反單一職責原則。現實中,選擇汽油還是電力是製造商(CarMaker)的決策,不應由汽車本身決定。Car 原始碼,違反開放封閉原則。透過建構子參數強制注入依賴,確保物件建立時即處於完整狀態。
class Car {
private final Energy energy;
public Car(Energy energy) {
this.energy = energy;
}
}
提供 Setter 方法,允許在物件建立後靈活替換依賴。
class Car {
private Energy energy;
public void setEnergy(Energy energy) {
this.energy = energy;
}
}
定義統一的注入介面,由容器統一調用,規範化注入契約。
interface EnergyConsumer {
void setEnergy(Energy energy);
}
class Car implements EnergyConsumer {
private Energy energy;
public void setEnergy(Energy energy) {
this.energy = energy;
}
}
提示:介面注入與 Setter 注入本質相似,差異在於前者強制實作特定介面,後者方法命名較自由。
| 框架 | 核心機制 | 適用場景 | 優缺點 |
|---|---|---|---|
| Spring | 運行時反射 + 動態代理 | 企業級應用、全棧開發 | 功能豐富、生態成熟;啟動較慢、記憶體佔用高 |
| Guice | 運行時註解掃描 + 反射 | 輕量級 Java 應用 | API 簡潔、啟動快;不適合 Android(反射效能問題) |
| Dagger / Hilt | 編譯時註解處理器生成輔助類 | Android 開發、效能敏感場景 | 編譯期檢查、零反射、極高效能;學習曲線陡峭 |
這是一個團隊架構決策,而非非黑即白的技術選擇:
建議:中小型專案或團隊初期,優先手動注入或輕量級容器;當模組依賴關係複雜、生命週期管理成為痛點時,再引入成熟框架。無論是否用框架,理解依賴倒置原則、養成面向介面編程習慣才是開發者的核心素養。
與依賴注入的「被動接收」不同,依賴查找屬於「主動獲取」:程式碼主動向容器請求所需物件(如 context.getBean("service"))。雖然同屬 IoC 實現,但會增加對容器 API 的侵入式依賴,現代開發中較少作為首選模式。