循环依赖 - 注入直接相互依赖的对象

时间:2014-06-06 07:16:38

标签: php dependency-injection circular-dependency di-containers

我已经使用Dice PHP DI container很长一段时间了,就注入依赖项的简单性而言,这似乎是最好的。

来自Dice Documentation

class A {
    public $b;

    public function __construct(B $b) {
        $this->b = $b;
    }
}

class B {

}

$dice = new \Dice\Dice;    
$a = $dice->create('A');
var_dump($a->b); //B object

但是,当你必须使用直接相互依赖的对象时,由于无限循环,finall结果是服务器错误。

示例:

class A {
    public $b;

    public function __construct(B $b) {
        $this->b = $b;
    }
}

class B {
    public $a;

    public function __construct(A $a) {
        $this->a = $a;
    }
}

Dice 的作者说没有办法从A或B类构造对象。为:

  • A' A'对象需要一个' B'在可以创建之前存在的对象
  • 但是' B'对象需要一个' A'在可以创建之前存在的对象

作者说,此限制涉及所有 DI容器


问题:

在不更改初始代码的情况下,克服此问题 很好 的最佳解决方案是什么?任何人都可以提供使用其他 DI容器的示例,当有可能运行 exampled代码 而不 笨重解决方法?

2 个答案:

答案 0 :(得分:5)

正如你在Dice github(https://github.com/TomBZombie/Dice/issues/7)上的帖子所提到的,解决这个问题而不删除循环依赖的唯一方法是重构其中一个类来使用setter注入:

class A {
    public $b;

    public function __construct(B $b) {
        $this->b = $b;
    }
}


class B {
    public $a;

    public function setA(A $a) {
        $this->a = $a;
    }
}

这允许构造对象:

$b = new B();
$a = new A($b);
$b->setA($a);

使用原始代码:

class A {
    public $b;

    public function __construct(B $b) {
        $this->b = $b;
    }
}

class B {
    public $a;

    public function __construct(A $a) {
        $this->a = $a;
    }
}

您无法构建它并遇到与容器相同的问题:

$b = new B(new A(new B(new A(new B(.............))))

使用诸如ReflectionClass :: newInstanceWithoutConstructor之类的hack使容器解决此问题的问题是您的对象现在依赖于使用此方法的创建逻辑。您基本上将代码耦合到容器,这是一个糟糕的设计,因为您的代码现在不再是可移植的,并且在没有容器执行对象构造的情况下无法使用。

答案 1 :(得分:4)

你有一个循环依赖,很难解决。首先要做的是通过重构类以及它们如何交互来尝试摆脱这种循环依赖

如果你真的无法做到,那就有解决方案。我会从Self-referencing models cause Maximum function nesting level of x in Laravel 4复制粘贴我的答案:

  • Setter injection

不是在构造函数中注入依赖项,而是可以将它注入setter中,该setter将在构造对象后调用。在伪代码中,看起来像这样:

$userRepo = new UserRepository();
$cartRepo = new CartRepository($userRepo);
$userRepo->setCartRepo($userRepo);
  • 懒惰注射

我不知道Dice是否支持延迟注入,但这也是一个解决方案:容器将注入代理对象而不是实际依赖项。该proxy-object仅在访问时才加载依赖项,因此在调用构造函数时无需构建依赖项。

如果您有兴趣,可以在下面解释懒惰注射的工作原理:http://php-di.org/doc/lazy-injection.html