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

Ili*_*sev 9 php dependency-injection circular-dependency di-containers

我已经使用了Dice PHP DI容器很长一段时间,它在注入依赖项的简单性方面似乎是最好的.

来自骰子文档:

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
Run Code Online (Sandbox Code Playgroud)

但是,当您必须使用直接相互依赖的对象时,由于无限循环,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;
    }
}
Run Code Online (Sandbox Code Playgroud)

Dice的作者说没有办法从A或B类构造一个对象.如:

  • 'A'对象需要'B'对象才能创建
  • 但是'B'对象需要'A'对象才能创建它

作者说,这个限制涉及所有 DI容器!


题:

在不改变初始代码的情况下,很好地克服这个问题的最佳解决方案是什么?任何人都可以提供一个使用其他DI容器的例子,什么时候可以运行exampled代码 而不需要庞大的解决方法?

Tom*_*m B 6

如您在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;
    }
}
Run Code Online (Sandbox Code Playgroud)

这允许构造对象:

$b = new B();
$a = new A($b);
$b->setA($a);
Run Code Online (Sandbox Code Playgroud)

用原始代码:

class A {
    public $b;

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

class B {
    public $a;

    public function __construct(A $a) {
        $this->a = $a;
    }
}
Run Code Online (Sandbox Code Playgroud)

您不能构造它并遇到与容器相同的问题:

$b = new B(new A(new B(new A(new B(.............))))
Run Code Online (Sandbox Code Playgroud)

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

  • 循环引用本身意味着两个对象共享一个责任,很可能一个(或两个)类做的太多(查看它们的依赖量,几乎可以肯定是这种情况,请参阅 http://misko .hevery.com/code-reviewers-guide/flaw-class-does-too-much/)。如果他们真的那么依赖对方,他们就是一个人的责任。 (3认同)

Mat*_*oli 5

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

如果您真的无法做到,也有解决方案。我将从自引用模型中复制粘贴我的答案,导致 Laravel 4 中 x 的最大函数嵌套级别

  • 二传手注入

与其在构造函​​数中注入依赖项,不如将其注入到 setter 中,该 setter 将在构造对象后调用。在伪代码中,它看起来像这样:

$userRepo = new UserRepository();
$cartRepo = new CartRepository($userRepo);
$userRepo->setCartRepo($userRepo);
Run Code Online (Sandbox Code Playgroud)
  • 懒惰注入

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

如果您有兴趣,这里有一个关于延迟注入如何工作的解释:http : //php-di.org/doc/lazy-injection.html