一尘不染

如果Singletons不好,那么服务容器为什么好?

php

我们都知道 单身 人士有多 糟糕
,因为他们隐藏了依赖关系以及其他原因

但是在一个框架中,可能有许多对象只需要实例化一次,并可以 从任何地方 调用(记录器,数据库等)。

为了解决这个问题,有人告诉我使用所谓的“对象管理器”(或诸如symfony之类的服务容器),在内部存储对服务(记录器等)的所有引用。

但是,为什么服务提供者不像单纯的Singleton那样糟糕?

服务提供者也隐藏了依赖关系,它们只是包装了第一个实例的创建。因此,我真的很难理解为什么我们应该使用服务提供商而不是单例。

PS。我知道不要隐藏依赖项,我应该使用DI(如Misko所述)

我要补充一点:如今,单例并没有那么邪恶,PHPUnit的创建者在这里对此进行了解释:

DI + Singleton解决了以下问题:

<?php
class Client {

    public function doSomething(Singleton $singleton = NULL){

        if ($singleton === NULL) {
            $singleton = Singleton::getInstance();
        }

        // ...
    }
}
?>

即使这不能解决所有问题,这还是很聪明的。

除了DI和Service Container 之外,还有什么好的可接受的 解决方案来访问此帮助对象?


阅读 278

收藏
2020-05-29

共1个答案

一尘不染

可以这么说,服务定位器只是两个弊端中的较小者。归结为这四个差异的“较小”( 至少我现在无法想到其他任何 差异):

单一责任原则

服务容器不违反Singleton那样的单一责任原则。单例混合了对象创建和业务逻辑,而服务容器严格负责管理应用程序的对象生命周期。在这方面,服务容器更好。

耦合

由于静态方法调用,单例通常被硬编码到您的应用程序中,这导致代码中的紧密耦合和难以模拟的依赖关系。另一方面,SL只是一类,可以注入。因此,尽管您所有的分类都将依赖它,但至少它是一个松散耦合的依赖关系。因此,除非您将ServiceLocator本身实现为Singleton,否则它会更好并且也更容易测试。

但是,所有使用ServiceLocator的类现在都将依赖于ServiceLocator,这也是一种耦合形式。可以通过使用ServiceLocator的接口来减轻这种情况,因此您不必绑定到具体的ServiceLocator实现,但是您的类将取决于某种Locator的存在,而根本不使用ServiceLocator会大大提高重用性。

隐藏的依赖

但是,非常存在隐藏依赖项的问题。当您仅将定位符注入到使用的类中时,您将不知道任何依赖项。但是与Singleton相比,SL通常会实例化幕后所需的所有依赖关系。因此,当您获取服务时,最终不会像CreditCard示例中的Misko
Hevery
那样结束,例如,您不必手动实例化依赖项的所有依赖关系。

从实例内部获取依赖关系也违反了Demeter定律,该定律指出您不应该深入研究协作者。实例只应与其直接的协作者交谈。Singleton和ServiceLocator都存在此问题。

全球状态

全局状态的问题也有所缓解,因为在测试之间实例化新的服务定位器时,所有先前创建的实例也会被删除(除非您犯了错误,并将其保存在SL的静态属性中)。当然,这对于SL管理的类中的任何全局状态都不成立。

另请参阅关于服务定位器与依赖注入的
Fowler的更深入讨论。


关于您的更新的注释以及Sebastian Bergmann关于使用Singletons的测试代码的链接文章:Sebastian丝毫没有暗示所建议的解决方法使使用Singleons的问题减少了。这只是使代码无法进行测试的一种方法。但这仍然是有问题的代码。实际上,他明确指出:“仅仅因为您可以,并不意味着您应该这样做”。

2020-05-29