Symfony 依赖注入容器(DIC)-如何处理过时的服务?
因此,Drupal使用基于Symfony2的依赖注入容器(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
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完成。您现在正在一个新的请求中操作,因为登录按钮将您链接到那里。您在一个全新的请求中运行,使用一个全新的重新构建的容器。没有人需要改变任何服务。