跳到主內容

依賴注入Dependency Injection (DI)

Dependency Injection (DI) 是一種設計模式,主要目的是將對象的依賴性注入到對象中,而不是在對象內部自行創建。這使得程式更加模組化、易於測試和維護。

Dependency Injection (DI) 的本質可以簡化為 "Object using Object",但更精確的描述是 "Object receiving its dependencies (other objects) from the outside"。這意味著一個物件依賴於另一個物件,但不是自己創建依賴,而是由外部提供"

Object using object" 是 DI 的核心,但 DI 的真正優勢在於外部提供依賴,實現鬆耦合與高靈活性。如果單純是物件使用其他物件,但依賴關係由內部創建,那麼就不是 DI,而是高耦合的設計。

這樣的設計方式強調物件之間的鬆耦合(low coupling),讓它們更容易測試和維護。

核心概念:

  1. 依賴性:一個對象需要的外部資源或服務。
  2. 注入:透過外部傳入(通常使用建構函式、屬性、或方法參數)。

PHP 範例

php

// 定義一個服務
class Logger {
    public function log($message) {
        echo "Log: $message\n"; }
}


// 使用 Logger 的類別
class UserService {
    private $logger; // 透過建構函式注入依賴


    public function __construct(Logger $logger) {
        $this->logger = $logger;
    }


    public function createUser($username) {
        // 使用注入的Logger
        $this->logger->log("User '$username' created.");
    }
}


// 手動注入
Logger $logger = new Logger();
$userService = new UserService($logger);
$userService->createUser("Nathan");

結果

pgsql

Log: User 'Nathan' created. 


C# 範例

csharp

// 定義一個服務
public class Logger {
    public void Log(string message) {
        Console.WriteLine($"Log: {message}");
    }
}


// 使用 Logger 的類別
public class UserService {
    private readonly Logger _logger;


    // 透過建構函式注入依賴
    public UserService(Logger logger) {
        _logger = logger;
    }


public void CreateUser(string username) {
        // 使用注入的 Logger
        _logger.Log($"User '{username}' created.");
    }
}


// 程式進入點 
class Program {
    static void Main(string[] args) {
        Logger logger = new Logger();
        UserService userService = new UserService(logger);
        userService.CreateUser("Nathan");
    }
}

結果

pgsql

Log: User 'Nathan' created. 


與 "Object Using Object" 的差異

沒有 DI 的設計:

php

class UserService {
    private $logger;


    public function __construct() {
        // 自行創建依賴
        $this->logger = new Logger();
    }


    public function createUser($username) {
        $this->logger->log("User '$username' created.");
    }
}

這樣的設計問題:

  1. 高耦合UserServiceLogger 被綁定在一起,無法輕易更換 Logger 的實現。
  2. 難以測試:如果需要模擬 Logger,就必須修改 UserService

使用 DI 的設計:

php

class UserService {
    private $logger;


    public function __construct($logger) {
        // 依賴由外部注入
        $this->logger = $logger;
    }


    public function createUser($username) {
        $this->logger->log("User '$username' created.");
    }
}

這樣的設計優點:

  1. 低耦合UserService 只關注如何使用 Logger,不關心它的實現細節。
  2. 可替換性:可以輕鬆更換不同的 Logger 實現。
  3. 可測試性:可以注入假(mock)物件來模擬 Logger 的行為。

總結

  • Dependency Injection 的核心在於避免類別內部直接依賴其他類別,改為由外部提供依賴。
  • 在 PHP 和 C# 中,建構函式注入是最常見的方式。
  • DI 使代碼更加模組化、可測試,且方便更換依賴實現(例如更換 Logger 為不同的實作)