服务定位器模式和抽象工厂模式有什么不同?

43
一开始看,服务定位器模式对我来说看起来与抽象工厂模式相同。它们似乎都有相同的用途(您查询它们以接收抽象服务的实例),并且在我阅读有关依赖注入的文章时都被提到。
但是,我看到服务定位器模式被描述为一个糟糕的想法, 但是至少有一个主要的依赖注入框架直接支持抽象工厂模式
如果它们不同,那么它们有什么区别?

这是链接:http://kill-0.com/duplo/2010/02/05/on-the-difference-between-abstract-factory-and-dependency-injectioninversion-of-control/。看起来你不是唯一一个想知道服务定位器和抽象工厂模式之间的区别的人 :) - Juliet
5个回答

48

在研究这些模式时,我遇到了同样的问题。我认为 Service Locator 和 Factory(无论它是否是抽象的)之间的主要区别在于:

Service Locator

  • “定位”一个现有的依赖项(服务)。虽然服务可能在解析过程中被创建,但对于客户端来说,这并不重要,因为:
  • 服务定位器的客户端不拥有依赖项。

Factory

  • 创建一个新实例的依赖项。
  • 工厂的客户端拥有依赖项。

Abstract Factory

  • 与常规工厂相同,只是不同的部署可以使用不同的抽象工厂实现,从而允许在这些不同的部署中实例化不同类型(甚至可以在运行时更改抽象工厂的实现,但通常不会这样使用)。

+1;关于目的不同的观点很好。我以为这一点从模式的名称就很明显了,所以我关注的是机制和公共接口,而不是使用意图。在返回的两个类的对象生命周期方面,这种区别非常重要。 - Merlyn Morgan-Graham
1
我认为意图是模式的一个定义特征。例如,装饰器和代理可以用相同的方式实现,但它们的意图不同。 - Gyan aka Gary Buyn
接受您的答案,因为模式的意图比接口如何支持该意图更重要。 - Merlyn Morgan-Graham
请提供代码示例来支持您所说的内容。这将使理解更加容易。 - Adi

13

根据我目前的了解,我认为两种模式的区别如下:

服务定位器模式(Service Locator pattern)

  • 明确支持注册应该创建/返回哪些具体对象
  • 通常具有通用接口,允许用户请求任何抽象类型,而不是特定类型
  • 可能本身就是具体实现

抽象工厂模式(Abstract Factory pattern)

  • 可能不支持注册——具体实现是否支持由特定实现决定,并且可能不会在抽象接口中公开
  • 通常具有多个获取特定抽象类型的方法
  • 本身不是具体实现(尽管当然会有具体实现)

我不确定我的答案是否正确,因为我对服务定位器模式没有太多经验。如果有人想贡献更具见地的答案,请随意发表。 - Merlyn Morgan-Graham
我现在对这些概念感到更加自在了。我的答案是正确的,但只是半个故事。意图更重要。请参见此问题的其他答案-https://dev59.com/h2025IYBdhLWcg3w9qyQ#9403827 (另外:我建议除了实现插件架构之外不要使用DI容器。在应用程序边界内使用手动构造函数注入,在需要它们的地方使用工厂。在主模块或拥有模块中构建对象树。使用服务定位器连接到真正不可靠的“服务”,而不是您的程序将拥有并完全控制以确保存在的进程/系统)。 - Merlyn Morgan-Graham

3
实际上,这两种模式之间存在明显的区别。众所周知,这两种模式都用于避免对具体类型的依赖。
然而,在阅读以下内容后:
- Rober C. Martin的《敏捷软件开发:原则、模式与实践》[书] - Martin Fowler的《控制反转容器和依赖注入模式》[文章]http://martinfowler.com/articles/injection.html - Mark Seemann的《模式识别:抽象工厂还是服务定位器?》[文章]http://blog.ploeh.dk/2010/11/01/PatternRecognitionAbstractFactoryorServiceLocator/ - Erich Gamma等人的《设计模式》[书]
出现了一些严重的矛盾:
Seemann说:“抽象工厂是一个通用类型,Create方法的返回类型由工厂本身的类型确定。换句话说,构造的类型只能返回单个类型的实例。”
虽然Robert C. Martin在他的书中没有提到通用类型,而且工厂示例允许使用键字符串作为参数来区分创建多种类型对象的实例。Gamma说抽象工厂的目的是“提供一个接口来创建相关或依赖对象的系列,而不指定它们的具体类”。值得一提的是,Gamma的抽象工厂示例违反了Martin所述的接口隔离原则(ISP)。ISP和SOLID原则总体上都是比较现代的原则,也许为了简单起见被省略了。Gamma和Martin的工作先于Seemann的工作,因此我认为他应该遵循已经制定的定义。虽然Fowler提出了服务定位器作为实现依赖反转的一种方式,但Seemann认为它是一种反模式。Gamma和Martin都没有提到服务定位器。然而,Seemann和Fowler都同意服务定位器需要一个配置步骤来注册具体类的实例,当请求那种对象的对象时,该实例将稍后返回。Martin或Gamma在其抽象工厂的定义中没有提到这个配置步骤。抽象工厂模式假定每次请求那种对象时都会实例化一个新对象。
结论
Service Locator和Abstract Factory的主要区别在于,Abstract Factory假设每次请求时都需要实例化并返回一个新对象,而Service Locator需要配置一个对象实例,并且每次都返回相同的实例。

我同意Seemann对Abstract Factory的看法与GoF的定义并不相符; 但你为什么说GoF的定义违反了ISP呢?其意图是让每个客户端都使用每个工厂方法。 - jaco0646

2

来源:http://blog.ploeh.dk/2010/11/01/PatternRecognitionAbstractFactoryorServiceLocator/

抽象工厂是一种通用类型,Create方法的返回类型由工厂本身的类型确定。换句话说,构造出的类型只能返回单个类型的实例。

另一方面,服务定位器是一个非泛型接口,具有一个泛型方法。单个服务定位器的Create方法可以返回无限数量的类型的实例。


0

Martin Fowler描述了几种服务定位器模式的实现方式,其中大多数是具体类,通过static方法进行配置和调用。我认为我们可以忽略这些变化,关注他使用依赖注入的最终示例,这是一种更现代的方法。我们可以将其与GoF书籍中的抽象工厂模式进行比较。

抽象工厂的区别特征是一组相关产品的固定集合。相反,服务定位器具有无限的不相关产品集合。这使得服务定位器更像一个黑盒子,并导致该模式的主要批评:它隐藏了API的依赖关系。当服务定位器作为动态注册表实现时,客户端可能会请求不存在的产品。这在抽象工厂中是不可能的。

在抽象工厂和服务定位器之间做出选择时,前者是更可取的模式。但这是一种虚假的二分法,因为还有更多的选择。这两种模式都依赖于依赖注入,但DI在没有它们的情况下运行得更好。

注入(Injection)是实现控制反转(Inversion of Control,IoC)的机制。IoC是依赖反转的理想形式,因为它最大程度地减少了客户端的复杂性并提供了最高级别的抽象化。服务定位器(Service Locator)和抽象工厂(Abstract Factory)是两种实现依赖反转的机制,但是它们都不具备控制反转的优点,并且从理想情况来看都是不可取的。

有关IoC与DI的更多信息,请参见:控制反转 vs 依赖注入
有关抽象工厂和服务定位器的缺陷信息,请参见:依赖注入代码味道


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接