现实生活中的依赖注入

Ind*_*ial 20 php oop singleton dependency-injection

我正在构建一个非常小的MVC框架来增加我的PHP知识并挑战自己.我已经到了类开始依赖彼此工作的地步. 依赖注入似乎是解决方案,并被一些大框架使用.

我在Github上发现了Bucket并且已经搞砸了一段时间才能理解基础知识.然而,我无法理解的是,什么时候创建一个容器是合适的?

制作一个大容器,包括可能需要的每个可能的课程,似乎对我来说只是适得其反,我无法想象这是一个好习惯.这似乎是至少糟糕表现的秘诀.

在替代方案中,即制造多个容器,我仍然不知道如何不再需要那些臭气熏天的单身人士.

假设我有以下代码:

$session_container = new bucket_Container();
$session_container->create('Database');
$session_container->create('Database_Sessions');

$log_container = new bucket_Container();
$log_container->create('Database');
$log_container->create('Database_Log');
Run Code Online (Sandbox Code Playgroud)

所以这里我们有两个容器,或者在这种情况下,用于两个完全不同的用法,它们是相互依赖的Database.

我的逻辑告诉我,上面的代码将创建Database-class的两个独立实例,这意味着我仍然必须使Database-class成为一个单例来确保我的数据库连接的并发实例没有发生?

它是否正确?

Joh*_*ohn 9

我不太了解具体的lib,但假设它允许你使用工厂,让工厂返回相同的实例.

编辑:好的,这只是在Bucket GitHub索引页面上.

class MyFactory {
  function new_PDO($container) {
    return new PDO("mysql:host=localhost;dbname=addressbook", "root", "secret");
  }
}

$bucket = new bucket_Container(new MyFactory());
$db = $bucket->get('pdo');
Run Code Online (Sandbox Code Playgroud)

所以在你的情况下,你可以简单地做:

class MyFactory {
   private $pdo;
   function new_Database($container) {
     if($this->pdo){
         return $this->pdo;
     }
     return $this->pdo = new PDO("mysql:host=localhost;dbname=addressbook", "root", "secret");
   }
}
$factory = new MyFactory();

$session_container = new bucket_Container($factory);
$session_container->create('Database_Sessions');

$log_container = new bucket_Container($factory);
$log_container->create('Database_Log');
Run Code Online (Sandbox Code Playgroud)

这样的事情.似乎不像火箭科学.

编辑2:我没有足够的重复点来评论这个问题(有点傻),但是为了回应你的"模块性"问题:把容器想象成你应用程序的"粘合剂".实际上,如果您有一个大型应用程序,您可能只想在应用程序的隔离部分"粘合".这是一个有效的封装问题.但即便如此,您仍然需要一个能够在最高抽象级别处理注入的容器.如果您只是为应用程序的每个部分创建一个单独的容器,您最终会得到不必要的实例重复,或者您必须应用另一级别的实例管理,这不会以任何方式改进封装:您仍然在应用程序的不同部分之间共享实例.

我的建议是在引导级别使用单个容器.如果您想为应用程序的特定部分(模块,插件等)添加封装,请使用"子容器".子容器从父容器继承实例,但父容器对该子容器一无所知(就他而言,他仍然是单身汉;)).可能是Bucket默认支持这个,我知道其他DI容器.如果没有,使用Decorator实现起来非常容易.想象一下这样的事情:

class MyContainerType extends bucket_Container {

    private $_parent;
    private $_subject;

    public function  __construct($factory = null, bucket_Container $parent = null){
        $this->_parent = $parent;
        $this->_subject = new bucket_Container($factory);
    }

    public function get($key){
        $value = $this->_subject->get($key);
        if($value){
            return $value;
        }
        return $this->_parent->get($key);
    }
    /**
     * Override and delegation of all other methods
     */
}
Run Code Online (Sandbox Code Playgroud)

  • 很好的答案,甚至认为创建称为终身管理器的东西是更好的做法,他们会为perrequest/perthread等实现逻辑,所以你不需要为每个"signleton"对象编写相同的代码(但不幸的是,bucket_Container**不支持它):`$ container-> create('Database',new RequestLifeTimeManager());` (2认同)

tro*_*skn 6

制作一个大容器,包括可能需要的每个可能的课程,似乎对我来说只是适得其反,我无法想象这是一个好习惯.这似乎是至少糟糕表现的秘诀.

反之.这正是你用di容器做的事情.容器只会按需实例化对象,因此通过它管理所有单例类几乎没有任何开销.

di的最大问题是区分共享对象(通常认为是单例的东西)和瞬态对象(通过正常应用程序流具有大量实例的对象).前者很容易通过di管理.后者并不适合.清楚地区分这两种"对象"可能看起来有点麻烦,但使用di容器确实是非常有益的副作用.