如何在简单的php函数中使用“依赖注入”,我应该打扰吗?


68

我一直听到人们在谈论依赖注入及其带来的好处,但是我并不真正了解它。

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

我尝试阅读有关Wikipedia的条目,但是该示例是用Java编写的,因此我不太了解它试图阐明的区别。(http://en.wikipedia.org/wiki/Dependency_injection)。

我读了这篇php-dependency-injection-in文章(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();

任何人都有很好的资源或独到的见解可以使方法和收益清晰明了?


1
你不需要在你的数据库的连接来传递-刚刚宣布作为全球的功能global $database_connection;
亚当霍普金森

Answers:


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文档的大量信息


2
感谢您提供了易于理解的快速入门PDO示例,并将其与实际问题的答案捆绑在一起:)
Aditya MP

2
作为第一个看到它的人,我感到很疯狂,但是这个(很大的反应)中的第一个单词不是正确的单词吗?
Thilo Savage

@ThiloSavage第一个提到它。谢谢。
Arkh 2012年

虽然答案确实很好地诠释了DI的基本原理,但第一句话是夸张的。
安德鲁

2
这是反模式的完美示例。您实际上并没有进行依赖注入,而只是传递了容器。这实际上隐藏了类的依赖关系,这与依赖关系注入点完全相反。
费利克斯·加侬-格尼尔


6

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

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


是的一种可能的实现。
莎拉(Sarah Happy)2010年

2

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

您在问题中列出的三个版本是典型的方法。第3版最接近我所见的行业发展方向。通过将创建依赖对象的职责移到类之外,您可以在测试代码中自由地随意操作它们。但是,我在使用该方法时遇到的问题是,您最终在构造函数中遇到了较长的依赖对象链,这些对象甚至可能无法被接收对象使用,而是传递给辅助依赖对象。它变得凌乱,您会丢失从何而来的知识。

@Arkh和@mmmshuddup的Dependency Container示例是一个很好的开始,但是我仍然发现该方法也有局限性。我到达的最终解决方案是定制的解决方案,该解决方案以Scala中流行的Cake Pattern为蓝本。它允许您将单个依赖项传递到每个构造函数中,并且可以定义每个类的依赖对象的默认构造。这使您摆脱了漫长的依赖链,也失去了对依赖的默认实现的控制。

我称系统为Diesel,我对此非常满意。我在github上为有兴趣的人发布了代码。您可以从我写的关于该主题的博客中找到它,该博客描述了基本用法以及对您的问题的更多详细信息。http://developers.blog.box.com/2012/02/15/introducting-diesel-php-dependency-injection/


2

依赖注入是删除两个组件之间的依赖关系的思想,以便关注它们为何相互依赖。

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

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

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

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

您的第一种情况更接近于依赖注入的概念。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.