什麼是 Dependency Injection (依賴注入)?

1
什麼是 Dependency Injection (依賴注入)?:依賴注入(Dependency Injection,DI)是軟體設計模式之一,透過外部容器或框架自動將物件所需依賴(服務、模組)「注入」到類別中,而非讓類別自行建立依賴,實現控制反轉(IoC)原則。它降低類別間耦合、提升可測試性與可維護性,是現代框架如 Spring、Angular、.NET Core 的核心機制。

什麼是 Dependency Injection (依賴注入)?

依賴注入(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)

依賴注入是專業軟體架構的基石,從簡單服務定位到企業級容器管理。它將「誰建立依賴」與「誰使用依賴」分離,讓程式碼如樂高積木般自由組合,提升測試性、可維護性與架構彈性,是從中級到高級開發者的必備技能。