如果单例不好,那么为什么服务容器是好的? [英] If Singletons are bad then why is a Service Container good?

查看:22
本文介绍了如果单例不好,那么为什么服务容器是好的?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们都知道糟糕的单身人士有多糟糕,因为它们隐藏了依赖关系,并且对于其他原因.

We all know how bad Singletons are because they hide dependencies and for other reasons.

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

But in a framework, there could be many objects that need to be instantiated only once and called from everywhere (logger, db etc).

为了解决这个问题,我被告知使用所谓的对象管理器"(或 Service Container 像 symfony),它在内部存储对服务的每个引用(记录器等).

To solve this problem I have been told to use a so called "Objects Manager" (or Service Container like symfony) that internally stores every reference to Services (logger etc).

但为什么服务提供者不像纯单例那样糟糕?

But why isn't a Service Provider as bad as a pure Singleton?

服务提供者也隐藏了依赖关系,他们只是包装了第一个实例的创建.所以我真的很难理解为什么我们应该使用服务提供者而不是单身人士.

Service provider hides dependencies too and they just wrap out the creation of the first istance. So I am really struggling to understand why we should use a service provider instead of singletons.

附注.我知道为了不隐藏依赖项,我应该使用 DI(如 Misko 所述)

PS. I know that to not hide dependencies I should use DI (as stated by Misko)

我要补充的是:如今单身人士并没有那么邪恶,PHPUnit 的创建者在这里解释了这一点:

I would add: These days singletons aren't that evil, the creator of PHPUnit explained it here:

DI + Singleton 解决问题:

DI + Singleton solves the problem:

<?php
class Client {

    public function doSomething(Singleton $singleton = NULL){

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

        // ...
    }
}
?>

即使这并不能解决所有问题,这也很聪明.

that's pretty smart even if this doesn't solve at all every problems.

除了 DI 和服务容器有什么好的可接受的解决方案来访问这个辅助对象?

Other than DI and Service Container are there any good acceptable solution to access this helper objects?

推荐答案

Service Locator 可以说是两害相权取其轻.归结为这四个差异的较小"(至少我现在想不出其他的):

Service Locator is just the lesser of two evils so to say. The "lesser" boiling down to these four differences (at least I can't think of any others right now):

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

Service Container does not violate Single Responsibility Principle like Singleton does. Singletons mix object creation and business logic, while the Service Container is strictly responsible for managing the object lifecycles of your application. In that regard Service Container is better.

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

Singletons are usually hardcoded into your application due to the static method calls, which leads to tight coupled and hard to mock dependencies in your code. The SL on the other hand is just one class and it can be injected. So while all your classed will depend on it, at least it is a loosely coupled dependency. So unless you implemented the ServiceLocator as a Singleton itself, that's somewhat better and also easier to test.

但是,所有使用 ServiceLocator 的类现在都将依赖于 ServiceLocator,这也是一种耦合形式.这可以通过使用 ServiceLocator 的接口来缓解,这样您就不会绑定到具体的 ServiceLocator 实现,但您的类将依赖于某种 ​​Locator 的存在,而根本不使用 ServiceLocator 会显着增加重用率.

However, all classes using the ServiceLocator will now depend on the ServiceLocator, which is a form of coupling, too. This can be mitigated by using an interface for the ServiceLocator so you are not bound to a concrete ServiceLocator implementation but your classes will depend on the existence of some sort of Locator whereas not using a ServiceLocator at all increases reuse dramatically.

虽然隐藏依赖的问题非常存在.当您只是将定位器注入消费类时,您将不知道任何依赖项.但与 Singleton 相比,SL 通常会实例化幕后所需的所有依赖项.所以当你获取一个服务时,你不会像 Misko Hevery在信用卡示例中,例如您不必手动实例化依赖项的所有依赖项.

The problem of hiding dependencies very much exists forth though. When you just inject the locator to your consuming classes, you wont know any dependencies. But in contrast to the Singleton, the SL will usually instantiate all the dependencies needed behind the scenes. So when you fetch a Service, you dont end up like Misko Hevery in the CreditCard example, e.g. you dont have to instantiate all the depedencies of the dependencies by hand.

从实例内部获取依赖项也违反了德米特法则,其中规定您不应该深入合作者.一个实例应该只与它的直接合作者交谈.这是 Singleton 和 ServiceLocator 的问题.

Fetching the dependencies from inside the instance is also violating Law of Demeter, which states that you should not dig into collaborators. An instance should only talk to its immediate collaborators. This is a problem with both Singleton and ServiceLocator.

全局状态的问题也得到了一定程度的缓解,因为当您在测试之间实例化一个新的服务定位器时,所有先前创建的实例也会被删除(除非您犯了错误并将它们保存在 SL 的静态属性中).当然,这不适用于由 SL 管理的类中的任何全局状态.

The problem of Global State is also somewhat mitigated because when you instantiate a new Service Locator between tests all the previously created instances are deleted as well (unless you made the mistake and saved them in static attributes in the SL). That doesnt hold true for any global state in classes managed by the SL, of course.

另请参阅 服务定位器与依赖注入 上的 Fowler,了解更多信息-深入讨论.

Also see Fowler on Service Locator vs Dependency Injection for a much more in-depth discussion.

关于您的更新和链接文章的说明Sebastian Bergmann 关于测试使用 Singletons 的代码 :Sebastian 并没有提出建议的解决方法可以减少使用 Singleons 的问题.这只是使原本无法测试的代码更具可测试性的一种方法.但它仍然是有问题的代码.事实上,他明确指出:仅仅因为你可以,并不意味着你应该".

A note on your update and the linked article by Sebastian Bergmann on testing code that uses Singletons : Sebastian does, in no way, suggest that the proposed workaround makes using Singleons less of a problem. It is just one way to make code that otherwise would be impossible to test more testable. But it's still problematic code. In fact, he explicitly notes: "Just Because You Can, Does Not Mean You Should".

这篇关于如果单例不好,那么为什么服务容器是好的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆