69

我一直听到人们谈论依赖注入及其好处,但我并不真正理解它。

我想知道这是否是“我一直将数据库连接作为参数传递”问题的解决方案。

我尝试阅读维基百科的条目,但该示例是用 Java 编写的,所以我并不完全理解它试图阐明的区别。(http://en.wikipedia.org/wiki/Dependency_injection)。

我阅读了这篇依赖注入在 PHP 中的文章(http://www.potstuck.com/2009/01/08/php-dependency-injection/),似乎目标是不将依赖项传递给对象直接,但要封锁对象的创建及其依赖项的创建。不过,我不确定如何在使用 php 函数的上下文中应用它。

此外,是否存在以下依赖注入,我是否应该费心尝试在功能上下文中进行依赖注入?

版本 1:(我每天创建但不喜欢的那种代码)

function get_data_from_database($database_connection){
    $data = $database_connection->query('blah');
    return $data;
}

版本 2:(不必传递数据库连接,但可能不是依赖注入?)

function get_database_connection(){
    static $db_connection;
    if($db_connection){
        return $db_connection;
    } else {
        // create db_connection
      ...
    }
}

function get_data_from_database(){
   $conn = get_database_connection();
   $data = $conn->query('blah');
   return $data;
}

$data = get_data_from_database();

版本 3:(“对象”/数据的创建是分开的,数据库代码仍然存在,所以这可能算作依赖注入?)

function factory_of_data_set(){
    static $db_connection;
    $data_set = null;
    $db_connection = get_database_connection();
    $data_set = $db_connection->query('blah');
    return $data_set;
}

$data = factory_of_data_set();

任何人都有一个很好的资源或只是使方法和好处变得清晰的洞察力吗?

4

5 回答 5

76

依赖注入是“我的构造函数中有更多参数”的一个大词。

当你不喜欢 globals 时,这就是你在可怕的 Singleton 浪潮之前所做的事情:

<?php
class User {
    private $_db;
    function __construct($db) {
        $this->_db = $db;
    }
}

$db   = new Db();
$user = new User($db);

现在,诀窍是使用单个类来管理您的依赖项,如下所示:

class DependencyContainer 
{
    private _instances = array();
    private _params = array();

    public function __construct($params)
    {
        $this->_params = $params;
    }

    public function getDb()
    {
        if (empty($this->_instances['db']) 
            || !is_a($this->_instances['db'], 'PDO')
        ) {
            $this->_instances['db'] = new PDO(
                $this->_params['dsn'],
                $this->_params['dbUser'], 
                $this->_params['dbPwd']
            );
        }
        return $this->_instances['db'];
    }
}

class User
{
    private $_db;
    public function __construct(DependencyContainer $di)
    {
         $this->_db = $di->getDb();
    }
}

$dependencies = new DependencyContainer($someParams);
$user = new User($dependencies);

你一定认为你只是另一个类和更复杂的。但是,您的用户类可能需要像许多其他类一样记录消息。只需将一个 getMessageHandler 函数添加到您的依赖容器中,并将一些$this->_messages = $di->getMessageHandler()添加到您的用户类中。其余代码无需更改。

你会得到很多关于symfony 的文档的信息

于 2010-03-31T14:48:07.270 回答
14

您的第一个示例依赖注入,您将对数据库对象的依赖注入到函数中。

莎拉说这不是,但我相信它是,我相信她正在考虑下一个级别的依赖注入容器:

http://components.symfony-project.org/dependency-injection/trunk/book/02-Dependency-Injection-Containers

于 2010-03-31T14:19:28.933 回答
6

您的示例都不像依赖注入,但版本一是最接近的。依赖注入是面向对象编程中使用的一种技术,其中对象的构造函数为其所需的服务对象提供参数,这些服务对象由实例的创建者(可以是工厂、测试或一个依赖注入框架)。

要解决“始终传递连接对象”问题,您可能需要考虑模板模式。模板模式基本上是一个抽象基类,具有重复代码块的公共部分,以及允许这些重复代码块实例之间变化的抽象方法。基本上基础是一段代码的模板,抽象方法是要填的空白。我个人使用模板方法模式在Java中做我的数据库资源控制。

于 2010-02-13T00:19:22.587 回答
2

我自己对这个主题(PHP 依赖注入)进行了很多搜索,但没有找到太多我喜欢的东西。关于其他语言的主题已经写了很多(Google Guice - http://code.google.com/p/google-guice/ ; Java Spring),但我找不到太多可用于 PHP 的内容。然而,无论使用哪种语言,挑战都是相似的。

您在问题中列出的三个版本是典型的方法。第 3 版是最接近我所看到的行业发展方向的版本。通过将创建依赖对象的责任转移到类之外,您可以在测试代码中随意操作它们。但是,我在使用这种方法时遇到的问题是,您最终会在构造函数中得到一长串依赖对象,这些对象甚至可能不会被接收对象使用,而是传递给辅助依赖对象。它变得混乱,你失去了从哪里来的知识。

@Arkh 和 @mmmshuddup 的 Dependency Container 示例是一个很好的开始,但我仍然发现这种方法的局限性。我得到的最终解决方案是一个定制的解决方案,它在某种程度上模仿了 Scala 中流行的蛋糕模式。它允许您将单个依赖项传递给每个构造函数,并允许您定义每个类的依赖对象的默认构造。这使您摆脱了长依赖链以及失去对依赖项的默认实现的控制。

我打电话给系统 Diesel,我对它非常满意。我在 github 上为任何感兴趣的人发布了代码。你可以从我写的关于这个主题的博客中找到它,它描述了基本用法,并更详细地介绍了你的问题。http://developers.blog.box.com/2012/02/15/introducting-diesel-php-dependency-injection/

于 2012-04-02T18:59:55.770 回答
2

依赖注入是消除两个组件之间的依赖关系的想法,以便专注于它们依赖的原因。

假设您有一个组件 A,它需要使用另一个组件 B 的服务。

如果您在 A 中硬编码 B 的存在,那么当您希望 A 使用相同的服务但​​由另一个组件实现时,您将陷入困境。

因此,通常,您定义一个 B 和 C 将实现的服务接口,并确保在使用 A 时,为它提供与所需接口兼容的对象。

在您的情况下,您可能会认为您的界面是一项可以进行查询的服务。

您的第一个案例是更接近依赖注入概念的案例。

于 2012-06-07T22:45:21.210 回答