依賴注入(Dependency Injection,DI)是軟體設計模式之一,透過外部容器或框架自動將物件所需依賴(服務、模組)「注入」到類別中,而非讓類別自行建立依賴,實現控制反轉(IoC)原則。它降低類別間耦合、提升可測試性與可維護性,是現代框架如 Spring、Angular、.NET Core 的核心機制。
依賴注入的核心概念
傳統方式中,類別直接 new 建立依賴,造成緊耦合:
# 緊耦合(傳統)
class OrderService:
def __init__(self):
self.payment = PaymentGateway() # 硬編碼依賴
self.email = EmailService()
def process_order(self):
self.payment.charge() # 無法單獨測試
依賴注入將依賴建立責任移到外部:
# 鬆耦合(DI)
class OrderService:
def __init__(self, payment: PaymentGateway, email: EmailService):
self.payment = payment
self.email = email
def process_order(self):
self.payment.charge()
self.email.send_confirmation()
依賴注入的三種實現方式
1. 建構式注入(Constructor Injection)
最常用方式,透過建構函式參數注入:
# Python
class OrderService:
def __init__(self, payment_gateway, email_service):
self.payment_gateway = payment_gateway
self.email_service = email_service
# 使用
order_service = OrderService(
StripeGateway(),
SendGridEmail()
)
2. Setter 注入(Setter Injection)
透過 setter 方法注入:
class OrderService:
def __init__(self):
self._payment = None
def set_payment_gateway(self, gateway):
self._payment = gateway
def set_email_service(self, service):
self._email = service
3. 介面注入(Interface Injection)
客戶端實作注入介面:
class Injectable:
def inject_dependencies(self, deps):
pass
class OrderService(Injectable):
def inject_dependencies(self, deps):
self.payment = deps['payment']
self.email = deps['email']
DI 容器(IoC Container)自動化
現代框架提供 DI 容器自動管理:
// Angular
@Component({
providers: [
{ provide: PaymentGateway, useClass: StripeGateway }
]
})
class OrderComponent {}
// Spring (Java)
@Configuration
public class AppConfig {
@Bean
public PaymentGateway paymentGateway() {
return new StripeGateway();
}
}
Python FastAPI 範例:
from fastapi import Depends
async def get_payment_gateway():
return StripeGateway()
@app.post("/orders")
async def create_order(payment: PaymentGateway = Depends(get_payment_gateway)):
payment.charge()
依賴注入的生命週期管理
容器管理物件生命週期:
| 生命週期 | 行為 | 使用場景 |
|---|---|---|
| Singleton | 全應用單例 | 資料庫連線、配置服務 |
| Transient | 每次請求新實例 | 短命業務物件 |
| Scoped | 請求範圍內單例 | Web 請求、HTTP Session |
# .NET Core appsettings.json services: - Singleton: ConfigurationService - Scoped: DatabaseContext - Transient: OrderValidator
實際優點與應用場景
優點:
-
鬆耦合:
OrderService不依賴StripeGateway具體類別 -
易測試:注入 Mock 物件進行單元測試
-
可配置:運行時切換實作(Stripe ↔ PayPal)
-
遵循 SOLID:依賴反轉原則(D)、介面隔離(I)
測試範例:
# 使用 pytest + pytest-mock
def test_order_service(mocker):
mock_payment = mocker.Mock()
mock_email = mocker.Mock()
service = OrderService(mock_payment, mock_email)
service.process_order()
mock_payment.charge.assert_called_once()
語言別 DI 框架
| 語言 | DI 框架 | 特色 |
|---|---|---|
| Java | Spring IoC | XML/註解配置,企業標準 |
| .NET | ASP.NET Core DI | 內建,生命週期管理 |
| JavaScript | Angular DI、InversifyJS | 階層式注入 |
| Python | FastAPI Depends、Injectify | 類型提示整合 |
| PHP | Laravel Container | 服務提供者 |
手動 vs 自動 DI
手動 DI(小型專案):
# factory.py
def create_order_service():
return OrderService(
StripeGateway(),
SendGridEmail()
)
自動 DI(大型專案):
# 容器自動解析依賴關係
container.register(PaymentGateway, StripeGateway)
container.register(OrderService) # 自動注入 PaymentGateway
service = container.get(OrderService)
常見問題與最佳實務
問題:
Service Locator(反模式)
class OrderService:
def get_payment():
return Container.get(PaymentGateway) # 隱藏依賴
明確依賴
class OrderService:
def __init__(self, payment: PaymentGateway):
self.payment = payment
最佳實務:
-
優先建構式注入(不可變依賴)
-
介面抽象,具體實作注入
-
明確生命週期管理
-
避免循環依賴(使用工廠或 Lazy)
依賴注入是專業軟體架構的基石,從簡單服務定位到企業級容器管理。它將「誰建立依賴」與「誰使用依賴」分離,讓程式碼如樂高積木般自由組合,提升測試性、可維護性與架構彈性,是從中級到高級開發者的必備技能。