Symfony 依赖注入容器(DIC)-如何处理过时的服务?

Symfony 依赖注入容器(DIC)-如何处理过时的服务?,symfony,dependency-injection,Symfony,Dependency Injection,因此,Drupal使用基于Symfony2的依赖注入容器(DIC)来组织其服务 此外,我自己也喜欢在较小的项目中使用这种模式(使用更简单的手工解决方案) 简而言之,它看起来是这样的: class Container { private $services = array(); function getService($key) { if (isset($this->services[$key])) { return $this->services[$key

因此,Drupal使用基于Symfony2的依赖注入容器(DIC)来组织其服务

此外,我自己也喜欢在较小的项目中使用这种模式(使用更简单的手工解决方案)

简而言之,它看起来是这样的:

class Container {
  private $services = array();
  function getService($key) {
    if (isset($this->services[$key])) {
      return $this->services[$key];
    }
    $method = 'create_' . $key;
    // @todo Check if method exists.
    // Call the method to lazy-create the service.
    return $this->services[$key] = $this->$method($key);
  }

  function create_kitchen() {
    // Kitchen depends on Stove.
    $stove = $this->getService('stove');
    return new Kitchen($stove);
  }

  function create_stove() {
    return new Stove();
  }
}

$container = new Container();
$kitchen = $container->getService('kitchen');
到目前为止还不错。
但是如果我想换一个新的炉子,而不换厨房怎么办

$kitchen = $container->getService('kitchen');
$kitchen->cookAnEgg();
$container->replace('stove', new BetterStove());
$kitchen->cookAnEgg();
我需要一个机制来替换厨房,让旧的厨房实例变得过时,或者我需要让厨房知道炉子已经被替换,这样第二个鸡蛋就可以用新炉子煮了

如果厨房想自己更换炉子呢

class Kitchen {
  private $stove;
  private $electrician;
  function __construct(Stove $stove, Electrician $electrician) {
    $this->stove = $stove;
    $this->electrician = $electrician;
  }
  function cookAnEgg() {
    while ($this->stove->isBroken()) {
      $this->electrician->installNewStove();
    }
    ..
  }
}
厨房是如何了解新炉子的

有没有处理这种情况的最佳做法

我会考虑使用观察者模式,但与DIC结合使用的最佳实践是什么

编辑:
我将其标记为Symfony2,但我假设它可以被视为一个更一般的问题,适用于所有类型的依赖注入容器

编辑二:
扩展了这个示例。

这是通过一些方法处理的:例如,在factory或builder中,您向它传递它需要的所有参数,以决定它应该返回哪种服务类型以及如何构造服务对象图

Symfony2已经实现了所有这些。我不认为自己做这件事有什么意义。您可以直接使用该组件,而不是使用整个symfony2堆栈

但如果这对你来说太大了,你也可以去

自己写可能会很有趣,因为有很多场合需要学习,但有时这只是重新发明轮子

编辑

Symfony容器的制作方式不允许在生产运行时对其进行修改,因此它不支持
$container->replace('hoose',new BetterStove())。这同样适用于运行时。在构建时,它是不同的,一旦定义了服务,就可以重新定义,但我知道您询问的是prod运行时。所以,一旦容器最终确定(构建),就不再对其进行更改

因此,在您的示例中,您有一个
炉子()
和一个
厨房(炉子)
。一段时间后,您添加了
BetterStove:hoose
,并保留了旧的
hoose()
厨房(hoose)
。答案是现在你会有两个厨房:一个是用旧的
炉子建造的,另一个是用新的
更好的
建造的

简单、直接的解决方案是,将“厨房”添加到“更好的”炉灶服务中,让容器的用户决定何时需要常规厨房,何时需要新的更好的炉灶

如果做出决策的逻辑(需要哪种厨房)很复杂,并且您不想在解决方案中重复它,那么您可以将它封装为一个工厂方法,其中包含做出此类决策所需的所有参数,然后容器的用户将查询工厂,并调用工厂的getKitchen(arg1,arg2…)witch将返回
Kitchen
,其中包含旧的常规炉子和新的BetterStove,具体取决于传递给工厂方法的参数


Ofc,在一个简单的手工解决方案中,你可以制作各种各样的可变“容器”,但至少我理解DYC,这不是重点-容器只是组装你的复合材料,不为他们做逻辑。

在我看来,依赖注入的意义在于,在创建服务定义之前,您可以决定哪个炉子最好,而使用它的代码完全不知道炉子是如何工作的

这使您能够将服务实现与调用代码分离。(从理论上讲,因为您仍然需要知道服务对象的接口,DIC对我们毫无帮助地隐藏了该接口。)

然后,您可以模拟一个炉子并将其放入容器中进行测试,而不会产生生产炉子可能具有的依赖关系

如果您在中流中切换服务,您就不再注入依赖项,您只是将容器用于其他模式


基本上:不要这样做。:-)找到导致需要不同炉子的配置,并在
*.services.yml
或编译过程中处理该依赖关系,和/或在不更改炉子接口的情况下改进炉子。

Drupal 8中的一个典型示例是,如果语言发生更改,很多现有的服务仍然使用旧语言。谢谢,但我不知道这是如何回答这个问题的。当然,会有一些工厂来创建服务。但问题是,如果我想替换它,我将如何将新实例传递给依赖它的其他服务?或者我如何确保仍使用旧实例的其他服务“退出游戏”。刚刚发现了一些东西!Drupal8中的一个典型示例是登录:一个提交处理程序让您登录,现在用户对象(以前是空的(或在空用户上)现在填充了一个用户对象。但是用户对象实际上不是一个服务!相反,当前_用户是AccountProxy,即使用户发生更改,它也可以保持相同的实例。因此,在上述情况下,服务可以是StoveProxy。提交处理程序让您登录,设置会话cookie,然后Drupal完成。您现在正在一个新的请求中操作,因为登录按钮将您链接到那里。您在一个全新的请求中运行,使用一个全新的重新构建的容器。没有人需要改变任何服务。