依賴注入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),讓它們更容易測試和維護。
核心概念:
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");
結果:
Log: User 'Nathan' created.
C# 範例
// 定義一個服務
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");
}
}
結果:
Log: User 'Nathan' created.
與 "Object Using Object" 的差異
沒有 DI 的設計:
class UserService {
private $logger;
public function __construct() {
// 自行創建依賴
$this->logger = new Logger();
}
public function createUser($username) {
$this->logger->log("User '$username' created.");
}
}
這樣的設計問題:
- 高耦合:
UserService
和Logger
被綁定在一起,無法輕易更換Logger
的實現。 - 難以測試:如果需要模擬
Logger
,就必須修改UserService
。
使用 DI 的設計:
class UserService {
private $logger;
public function __construct($logger) {
// 依賴由外部注入
$this->logger = $logger;
}
public function createUser($username) {
$this->logger->log("User '$username' created.");
}
}
這樣的設計優點:
- 低耦合:
UserService
只關注如何使用Logger
,不關心它的實現細節。 - 可替換性:可以輕鬆更換不同的
Logger
實現。 - 可測試性:可以注入假(mock)物件來模擬
Logger
的行為。