PHP中的依赖注入:面向初学者
想象一下你在组装一台电脑。你不会把处理器永久焊在主板上——而是将其插入一个专用插槽,以便在需要时更换为更强大的型号。PHP中的依赖注入(DI)遵循同样的原则:不在类内部创建依赖,而是从外部“注入”它们。
在本文中,我们将探讨什么是依赖注入、为什么需要它,以及如何在PHP中应用它。你将了解如何使代码更灵活、更易于测试和维护。
用简单的话说什么是依赖注入
依赖注入是一种设计模式,对象从外部获取其依赖(其他对象、服务),而不是自己创建它们。与其在类内部写new Database(),不如通过构造函数或setter方法传入一个现成的数据库对象。
让我们看看DI解决的问题。考虑一个直接创建数据库连接的UserService类:
<?phpclass UserService { private $db; public function __construct() { // 与具体实现紧密耦合 $this->db = new MySQLConnection('localhost', 'user', 'pass'); } public function getUser($id) { return $this->db->query("SELECT * FROM users WHERE id = $id"); }}?>这种方法的缺点显而易见:
- 紧耦合——
UserService类与MySQLConnection绑定。要切换到PostgreSQL,必须修改类的代码。 - 测试困难——在单元测试中无法将数据库替换为mock对象。
- 违反单一职责原则——类不仅处理用户相关操作,还负责创建数据库连接。
现在用依赖注入重写这个类:
<?phpinterface DatabaseInterface { public function query($sql);}
class MySQLConnection implements DatabaseInterface { public function query($sql) { // MySQL的实现 }}
class PostgreSQLConnection implements DatabaseInterface { public function query($sql) { // PostgreSQL的实现 }}
class UserService { private $db; // 通过构造函数注入依赖 public function __construct(DatabaseInterface $db) { $this->db = $db; } public function getUser($id) { return $this->db->query("SELECT * FROM users WHERE id = $id"); }}
// 使用$db = new MySQLConnection('localhost', 'user', 'pass');$userService = new UserService($db);?>现在UserService不知道它具体使用哪个数据库。它只使用DatabaseInterface接口。这就是依赖注入的本质。
三种主要的依赖注入类型
在PHP中,有三种主要的依赖注入方式。我们来逐一了解。
1. 构造函数注入
这是最常见且推荐的方式。依赖通过构造函数参数传递,并保存在类的私有属性中。
<?phpclass Logger { private $writer; public function __construct(LogWriterInterface $writer) { $this->writer = $writer; } public function log($message) { $this->writer->write($message); }}?>优点:依赖关系清晰可见,对象始终处于有效状态(所有依赖都是必需的)。
缺点:如果