管理全局依赖关系

时间:2016-01-06 03:36:28

标签: php design-patterns dependency-injection

我正在编写一个相当大的应用程序,并开始研究不同的设计模式来编写“更好的代码”。我已经开始使用PHPUnit并且发现我的代码在没有依赖注入的情况下不容易测试,因为如果没有它,我将无法注入模拟类。我很喜欢依赖注入的想法。

我遇到的问题理解更多地与项目中的依赖项管理有关。具体来说,那些我会做全局的,比如数据库连接。所以现在,我必须将其作为一个依赖项注入而不是一个全局变量 - 这很好。

但是,如果我在实例化数据库对象之间进行了大量的函数调用,并且实际需要它,那么这就是我开始有点困惑的地方。对我来说,每个函数都依赖于数据库对象是没有意义的。将它放在具有所有全局依赖关系的依赖容器中并且我的代码中的每个函数都依赖于容器也没有意义。

想象一下嵌套的函数调用,如下所示。

<?php

    function foo()
    {
        bar();
    }

    function bar()
    {
        // Now we need to inject an instance of Database, but we don't have it!
        baz();
    }

    function baz(Database $database)
    {
        // work with the database here
    }

    $database = new Database();
    foo();

我可以初始化我的数据库,我甚至可以把它放在容器中。数据库类只会创建一次,并且对应用程序来说是全局的。但是使用上面的代码,如果baz执行某些数据库操作,现在我必须将数据库作为实例传递给foo和bar。如果我将数据库放在容器中,那么我将传递容器。然而,foo,或许,bar可能无需关心baz正在使用数据库。由于Database类的性质,每次我需要使用它时实例化数据库都是不好的设计。

我已经考虑过如何解决这个问题,我的一个想法是使用静态:: getInstance方法,但是我在DI上读的越多,我越喜欢这种方法,我觉得这是错的并且我会以错误的方式解决这个问题。

我能看到的另一个选择是拥有一个容器类,该容器类具有纯静态的get / set方法,以及一个包含数据的静态私有数组。然而,我在那里实现的只是封装了全局范围。

我应该如何管理我的全局依赖关系,以及从实例化到注入的链接?

1 个答案:

答案 0 :(得分:1)

您将获得与此处用户一样多的意见;) 我们在公司使用以下方法。

我们有一个全球工厂类。该类负责管理依赖项。显然,它很大,因为它处理所有可能对象的实例化,但很容易测试。 好的是,这个工厂的大多数方法都是私有的。因为您只需要最顶层的对象。其他一切都被隐藏了。

以下是数据库的示例。您想从DB收集所有产品。

class ProductCollector
{
    private $repository;

    public function __construct(ProductRepository $repository)
    {
        $this->repository = $repository;
    }

    public function collect()
    {
        $collection = $this->repository->getAllProducts();
        // Do something with collection
        // ...
        return $collection;
    }
}

class ProductRepository
{
    private $pdo;

    public function __construct(\PDO $pdo)
    {
        $this->pdo = $pdo;
    }

    public function getAllProducts()
    {
       //... Here you can use PDO
    }
}

class Factory
{
    public function createProductCollector()
    {
        return new ProductCollector($this->createProductRepository());
    }

    private function createProductRepository()
    {
        return new ProductRepository($this-createDbConnection());
    }

    private function createDbConnection()
    {
        // For the sake of this example we don't care how you get the DB credentials into the factory.
        return new \PDO('mysql:dbname=testdb;host=127.0.0.1', 'username', 'password');
    }
}

现在,在您的代码中,您可以执行以下操作:

$factory = new Factory();

$collector = $factory->createProductCollector();
$collection = $collector->collect();

这当然只是处理DI的众多可能解决方案之一。