【问题标题】:PHP How to prevent pimple DIC causing infinite loop on a circular dependencyPHP如何防止疙瘩DIC导致循环依赖的无限循环
【发布时间】:2017-03-08 04:04:53
【问题描述】:

在此示例中,我将 classAclassB 与疙瘩容器一起使用。

它们都相互依赖。但是,当使用 pimple DIC 进行设置时,以下代码会导致无限循环...

必须有一种方法可以在 pimple 中执行此操作,但我在文档中看不到它...任何想法如何防止无限循环?

// PIMPLE CONTAINER
use Pimple\Container;
$container = new Container();

use Classes\ClassA;
$container['ClassA'] = function ($c) {
  return new ClassA($c['ClassB']);
};

use Classes\ClassB;
$container['ClassB'] = function ($c) {
  return new ClassB($c['ClassA']);
};

【问题讨论】:

  • pimple DIC是真的吗?
  • 这都是同一个文件吗?文件中散布的“用途”并不清楚。

标签: php oop dependency-injection pimple


【解决方案1】:

@yivi 首先对循环引用的有效性进行了有效的观察in their answer。所以我真的认为你应该在这里评估你的设计。您可能正在治疗症状,而不是根本问题。尽管给出了您的通用示例代码(也就是说,这是解决手头问题的好代码),但我们不可能对此发表评论。如果您认为值得一试,也许可以对您的设计提出一个新问题?也许在这里或Code Review 上?

如果您控制ClassAClassB 的设计,那么对这类事情的规定方法是使用setter 注入,而不是构造函数注入。

这应该可行:

// PIMPLE CONTAINER
use Pimple\Container;
$container = new Container();

use Classes\ClassA;
$container['ClassA'] = function ($c) {
  return new ClassA();
};

use Classes\ClassB;
$container['ClassB'] = function ($c) {
  return new ClassB();
};


$container->extend('ClassA', function ($instanceOfA, $container) {
    $instanceOfA->setB($container['ClassB']);

    return $instanceOfA;
});

$container->extend('ClassB', function ($instanceOfB, $container) {
    $instanceOfB->setA($container['ClassA']);

    return $instanceOfB;
});

(未经测试,可能包含拼写错误,但这是一般要点)

请注意原始构造函数如何不再使用依赖项,将其插入留给特定的 setter。这允许您在容器中创建服务,然后使用 extend 调用相关的 setter 以在此之后插入依赖项。

这在文档中:Modifying Services after Definition

【讨论】:

  • 非常感谢@Yivi 和 Adam Cameron 的回复。你们都提到了这个循环构造函数依赖问题更多地归结为糟糕的设计,这是可能的。我的场景是 - 我有一个 Query 类和 Validation 类。 Query 类有许多方法需要通过验证类来验证数据。其中一种验证方法需要查询类中的方法。因此,我是否有更好的方法来构建代码?..或者如果我使用您的一种解决方案来解决上述情况,是否可以。再次感谢您的回复。
  • @jon 就像我建议的那样:提出一个单独的问题,我想。这一点与 this 问题并不真正相关,而且它会给你更多的操作空间。您还可以将它的上下文从 Pimple、DI 甚至 PHP 扩展到它是一个通用的 OO 设计问题。我建议您使用实际的方法名称和用法来实现存根类,以更好地对情况进行上下文化:在这种情况下,名称和用法对于设计决策很重要。顺便说一句,您的设计可能是正确的。很难说。
  • 似乎这个解决方案不起作用。 extend('ClassA' 在 ClassA 拳头使用后直接调用。然后,ClassB constructor 被调用,然后是需要 ClassA 的 ClassB extend。导致循环。更好地修复类设计!
【解决方案2】:

严格来说,你的问题与痘痘无关。

你只是有一个循环构造函数依赖,没有办法解决这些问题。 A需要B需要A需要B...

问题是,通常,两个相互依赖的类没有多大意义。但是在很少确实发生这种情况的情况下,两者之一应该是较轻的依赖项,在这种情况下,您可以通过 setter 或类似的机制注入对象在实例化后

通过传递容器并在ClassB 内部实例化,您隐藏了您的依赖项,这超出了拥有依赖项注入容器的目的。现在ClassB 依赖于Container,而不是ClassA,这正是你想要的。

不这样做,只需将方法 setA(Class A $a) 添加到您的 ClassB

然后,当你真正需要它时,你通过调用$b->setA($container['ClassA']); 来注入你的依赖,而不是在 DI 容器上。作为 Adam points out,您甚至可以通过扩展您的服务并在服务定义中使用 setter 来在您的容器中进行 setter 注入。

但再次重申,您的主要问题是存在循环依赖。再想想。这非常很可能表明您的设计可以做一些改进。

【讨论】:

  • 这可能不是一个疙瘩问题,但它可以通过 Pimple 解决。看我的回答。我认为你和我的组合很有用,所以我会在我的一个中交叉引用你的一个。
  • @Adam,是的,我打算建议在服务定义中调用 setter,但我仍然认为他的主要问题是有一个循环 dep。 IMO(很多时候)往往表明设计有其他问题。不过,我喜欢你的回答。 :)
  • 是的。我在回答中也再次强调了这一点。良好的团队合作!
【解决方案3】:
// PIMPLE CONTAINER
use Pimple\Container;
$container = new Container();

use Classes\ClassA;
$container['ClassA'] = function ($c) {
  return new ClassA($c['ClassB']);
};

use Classes\ClassB;
$container['ClassB'] = function ($c) {
  return new ClassB($c);
};

不是从 classB 实例化 ClassA,而是将容器传递给 ClassB。然后当您需要 ClassB 中的 ClassA 时,您可以使用您传递给它的容器来启动/获取 ClassA 的实例。

【讨论】:

    猜你喜欢
    • 2013-07-18
    • 1970-01-01
    • 2021-09-27
    • 1970-01-01
    • 2021-07-25
    • 1970-01-01
    • 2021-01-08
    • 2017-04-13
    相关资源
    最近更新 更多