1. 依赖注入的本质与价值
依赖注入(Dependency Injection,简称DI)是现代PHP开发中最重要的设计模式之一。它远不止是一种"传参技巧",而是一种从根本上改变代码组织方式的架构哲学。让我们从一个真实案例开始理解DI的价值。
去年我接手过一个电商项目,其中的订单服务类是这样写的:
php复制class OrderService {
private $db;
private $logger;
private $mailer;
public function __construct() {
$this->db = new MySQLDatabase();
$this->logger = new FileLogger('/var/log/app.log');
$this->mailer = new SendGridMailer(API_KEY);
}
public function createOrder($orderData) {
// 业务逻辑...
}
}
这段代码暴露了典型的问题:当我们需要更换邮件服务商,或者想在测试中使用内存数据库时,都不得不修改OrderService的源代码。这就是所谓的"硬编码耦合"问题。
1.1 控制反转原则
DI的核心是实现控制反转(Inversion of Control,IoC)。传统模式下,对象自己控制依赖的创建;而在DI模式下,这种控制权被反转了 - 依赖由外部提供,对象只是被动接收。
php复制class OrderService {
private $db;
private $logger;
private $mailer;
public function __construct(
DatabaseInterface $db,
LoggerInterface $logger,
MailerInterface $mailer
) {
$this->db = $db;
$this->logger = $logger;
$this->mailer = $mailer;
}
}
这种改变带来了几个关键优势:
- 可替换性:可以轻松切换不同的实现(如测试时用MockMailer)
- 可测试性:单元测试时可以注入测试替身
- 关注点分离:OrderService只需关注业务逻辑,不关心依赖如何创建
1.2 依赖抽象的威力
注意上面代码中的类型提示都是接口(Interface),这是DI的另一个关键点:依赖抽象,而非具体实现。这种抽象让我们可以在不修改业务代码的情况下:
- 开发环境使用FileLogger
- 生产环境使用ELKLogger
- 测试环境使用MemoryLogger
只需要在容器配置中更改绑定关系即可。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 依赖注入的三种实现方式
2.1 构造器注入(推荐方式)
构造器注入是最常用、最推荐的方式。它的特点是将所有必需依赖通过构造函数传入:
php复制class PaymentProcessor {
private $gateway;
private $logger;
public function __construct(
PaymentGatewayInterface $gateway,
LoggerInterface $logger
) {
$this->gateway = $gateway;
$this->logger = $logger;
}
}
优点:
- 依赖关系明确
- 对象创建后即处于可用状态
- 依赖不可变(immutable),线程安全
- 易于测试
2.2 Setter方法注入
Setter注入适
