面向初学者的 PHP 依赖注入(Dependency Injection)

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

什么是 PHP 中的依赖注入以及为什么需要它?

\n\n

想象一下你在建造一所房子。与其自己制作每一块砖,你只需订购它们并使用现成的。编程中的 依赖注入(Dependency Injection,DI) 遵循同样的原则——你不在类内部创建对象,而是从外部获取它们。这使你的代码变得灵活、可测试且易于理解。

\n\n

在 PHP 中,如同任何面向对象语言一样,类通常依赖于其他类。当依赖关系直接在类内部创建时,问题就出现了——这被称为紧耦合(tight coupling)。依赖注入通过从外部传递依赖关系来解决这个问题。

\n\n

没有依赖注入的问题

\n

考虑一个典型示例,其中 UserService 类自己创建了一个数据库对象:

\n\n
<?php\nclass Database {\n    public function query($sql) {\n        // 查询逻辑\n        return "结果: " . $sql;\n    }\n}\n\nclass UserService {\n    private $db;\n    \n    public function __construct() {\n        // 紧耦合——类自己创建依赖\n        $this->db = new Database();\n    }\n    \n    public function getUser($id) {\n        return $this->db->query("SELECT * FROM users WHERE id = $id");\n    }\n}\n\n$service = new UserService();\necho $service->getUser(1);\n?>
\n\n

这种方法的缺点:

\n
    \n
  • 无法将 Database 替换为其他实现(例如用于测试)
  • \n
  • 难以测试——无法插入模拟对象(mock)
  • \n
  • 代码变得庞大且难以扩展
  • \n
\n\n

解决方案:通过构造函数注入依赖

\n

现在,我们使用依赖注入重写同一个类:

\n\n
<?php\nclass UserService {\n    private $db;\n    \n    // 依赖从外部传递\n    public function __construct(Database $db) {\n        $this->db = $db;\n    }\n    \n    public function getUser($id) {\n        return $this->db->query("SELECT * FROM users WHERE id = $id");\n    }\n}\n\n// 单独创建依赖\n$database = new Database();\n$service = new UserService($database);\necho $service->getUser(1);\n?>
\n\n

现在 UserService 不知道 Database 是如何创建的。它只是使用传递给它的东西。这就是依赖注入。

\n\n

PHP 中依赖注入的类型

\n

有三种主要的依赖注入方式。我们逐一举例说明。

\n\n

1. 构造函数注入(Constructor Injection)

\n

这是最常见且推荐的方式。依赖关系在对象创建时通过构造函数传递。

\n\n
<?php\nclass Logger {\n    public function log($message) {\n        echo "[日志]: " . $message;\n    }\n}\n\nclass OrderProcessor {\n    private $logger;\n    \n    // 构造函数中的依赖\n    public function __construct(Logger $logger) {\n        $this->logger = $logger;\n    }\n    \n    public function process($order) {\n        // 处理订单\n        $this->logger->log("订单已处理: " . $order);\n    }\n}\n\n$logger = new Logger();\n$processor = new OrderProcessor($logger);\n$processor->process("订单 #123");\n?>
\n\n

优点:依赖始终可用,对象始终处于有效状态。缺点:如果依赖过多,构造函数可能会变得臃肿。

\n\n

2. Setter 注入(Setter Injection)

\n

当依赖可能是可选的,或者需要在对象运行过程中替换时使用。

\n\n
<?php\nclass MailService {\n    private $transport;\n    \n    // 依赖的 setter 方法\n    public function setTransport($transport) {\n        $this->transport = $transport;\n    }\n    \n    public function send($to, $mess                

博客

书籍推荐