PHP疙瘩交叉依赖

时间:2013-07-19 07:11:03

标签: php dependency-injection dependencies service-locator pimple

我有两个相互依赖的课程:

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

我需要像这样把它们包裹起来:

$c = new \Pimple();
$c['aService'] = function($c){
    return new A($c['bService']);
}
$c['bService'] = function($c){
    return new B($c['aService']);
}

但不幸的是我骑自行车了:

Fatal error: Maximum function nesting level of '100' reached, aborting!

有没有办法在没有骑车的情况下达到这种交叉依赖性?或者我只能使用单向依赖?

1 个答案:

答案 0 :(得分:1)

这让我想起 baboushka的

当然,你必然会在这里得到无限的递归。两个函数互相调用,每次返回一个新实例,即将调用的返回值传递给它们的函数计数器部分,然后再调用该函数,再次调用另一个函数,调用....
结论:当你从一开始就有两个相互依赖的类时(__construct),你的设计可能存在缺陷。

您定义两个构造函数的方式,您将永远无法创建类的实例。只是因为你需要同时实例化这两个类 你不能,你只是不能那样做。

试试这个:

class A
{
    public $b = null;
    public function __construct(B $b = null)
    {
        $this->b = $b;
    }
    public function setB(B $b = null)
    {
        if ($b === null)
        {
            $b = new B($this);//pass A here
        }
        $this->b = $b;
        return $this;
    }
}
class B
{
    public $a = null;
    public function __construct(A $a = null)
    {
        $this->setA($a);
    }
    public function setA(A $a = null)
    {
        if ($a === null)
        {
            $a = new A($this);//pass B here
        }
        $this->a = $a;
        return $this;
    }
}

通过将构造函数参数的默认值设置为null,传递实例已成为可选,因此现在可以执行此操作:

$a = new A;
$b = new B($a);
//or even:
$bFromA = $a->b;

BTW:总是事先声明你的属性。 It'll speed up your classes

就个人而言,我会使用getter 一个setter,并且延迟加载依赖项,但我会按原样保留构造函数:

class A
{
    //protected, or private. Access this via getter/setter
    protected $b = null;
    public function __construct(B $b = null)
    {
        $this->setB($b);
        return $this;
    }
    //setter, allows injection later on
    public function setB(B $b = null)
    {
        $this->b = $b;//allow to set to null
        return $this;
    }
    //getter, lazy-loader:
    public function getB()
    {
        if ($this->b === null)
        {//create new instance, if b isn't set
            $this->setB(
                new B($this)
            );
        }
        return $this->b;
    }
}
class B
{
    protected $a = null;
    public function __construct(A $a = null)
    {
        $this->setA($a);
        return $this;
    }
    public function setA(A $a = null)
    {
        $this->a = $a;
        return $this;
    }
    public function getA()
    {
        if ($this->a === null)
        {
            $this->setA(
                new A($this)
            );
        }
        return $this->a;
    }
}

使用Pimple:

$c['aService'] = function($c)
{
    return new A;
};
$c['bService'] = function($c)
{
    return new B;
};
$b = $c->bService;
$b->getA();//works just fine