【问题标题】:Managing global dependencies管理全局依赖项
【发布时间】:2016-01-06 03:36:28
【问题描述】:

我正在编写一个相当大的应用程序,并开始研究不同的设计模式来编写“更好的代码”。我从 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 方法,以及一个包含数据的静态私有数组。然而,我在那里实现的只是封装了全局范围。

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

【问题讨论】:

    标签: php design-patterns dependency-injection


    【解决方案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 的众多可能解决方案之一。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-09-04
      • 2015-03-20
      • 1970-01-01
      • 2012-09-23
      • 2011-10-04
      • 2017-12-24
      • 1970-01-01
      • 2013-05-17
      相关资源
      最近更新 更多