可以这么说,服务定位器只是两个弊端中的较小者。归结为这四个差异的“较小”( 至少我现在无法想到其他任何 差异):
单一责任原则
服务容器不违反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的问题减少了。这只是使代码无法进行测试的一种方法。但这仍然是有问题的代码。实际上,他明确指出:“仅仅因为您可以,并不意味着您应该这样做”。



