现实生活中的依赖注入

时间:2011-02-19 12:21:02

标签: php oop dependency-injection singleton

我正在构建一个非常小的MVC框架来增加我的PHP知识并挑战自己。我已经到了类开始依赖彼此工作的地步。 Dependency injection似乎是解决此问题的方法,并被big frameworks周围的一些人使用。

我在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');

所以这里我们有两个容器,或者在本例中是 buckets ,它们是两个完全不同的用法,它们依赖于Database类。

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

这是正确的吗?

3 个答案:

答案 0 :(得分: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');

所以在你的情况下你可以做到:

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');

这样的事情。看起来不像火箭科学。

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

我的建议是在引导程序级别使用单个容器。如果您想为应用程序的特定部分(模块,插件等)添加封装,请使用“子容器”。子容器从父容器继承实例,但父容器对该子容器一无所知(就他而言,他仍然是单身汉;))。可能是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
     */
}

答案 1 :(得分:6)

  

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

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

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

答案 2 :(得分:0)

如果您担心多个同时连接,可以使用mysql_pconnect()或equivelant来处理您正在使用的数据库。它将检查连接是否已打开,如果是,则使用现有连接。

就容器问题而言,我已经看到它以两种方式完成,您似乎都知道这两种方式。第一种方法是让框架读取您的数据库模式并在每个表之前创建类。我个人不喜欢这种方法。 Symfony就是这样做的一个框架(通过使用ORM原则)。

我看到的更优选的方法是拥有一个通用容器,它基本上为你提供一个表,列和一个动作的sql。这是codeIgniter采用的方法:

$query = $this->db->get('mytable');
$query = $this->db->get_where('mytable', array('id' => $id), $limit, $offset);