管理全局依赖关系


Managing global dependencies

我正在编写一个相当大的应用程序,并已开始研究不同的设计模式以编写"更好的代码"。我从PHPUnit开始,发现如果没有依赖注入,我的代码将不容易测试,因为如果没有它,我将无法注入mock类。我对依赖注入的想法很满意。

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

然而,如果在数据库对象的实例化和实际需要它的地方之间有大量的函数调用,这就开始让我感到有点困惑。然后每个函数都依赖于数据库对象,这对我来说是没有意义的。对我来说,把它放在一个包含所有全局依赖项的依赖性容器中,并让代码中的每个函数都依赖于容器,这也没有意义。

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

<?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类的性质,每次需要使用数据库时都实例化它,这将是一个糟糕的设计

我曾想过如何解决这个问题,我的想法之一是使用static::getInstance方法,然而,我在DI上读得越多,我就越不喜欢这个方法,我只是觉得它是错误的,我正在以错误的方式进行。

我可以看到的另一个选项是有一个容器类,它将具有纯静态的get/set方法,以及一个包含数据的静态私有数组。然而,我在那里所实现的只是封装全球范围。

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

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

我们有一个全球性的工厂类。这个类负责管理依赖关系。很明显,它很大,因为它处理所有可能对象的实例化,但很容易测试。好的是,这个工厂里的大多数方法都是私有的。因为你只需要最上面的对象。其他一切都隐藏起来了。

下面是一个数据库示例。您希望从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的众多可能解决方案之一。