服务定位器模式与抽象工厂模式有啥不同吗?

Posted

技术标签:

【中文标题】服务定位器模式与抽象工厂模式有啥不同吗?【英文标题】:Is the Service Locator pattern any different from the Abstract Factory pattern?服务定位器模式与抽象工厂模式有什么不同吗? 【发布时间】:2011-08-07 13:39:00 【问题描述】:

乍一看,服务定位器模式在我看来与抽象工厂模式相同。它们似乎都有相同的用途(您查询它们以接收抽象服务的实例),并且在我阅读依赖注入时都提到了它们。

不过,I have seen the Service Locator pattern described as a poor idea,却见过direct support for the Abstract Factory pattern in at least one major Dependency Injection framework。

如果不一样,有什么区别?

【问题讨论】:

给你:kill-0.com/duplo/2010/02/05/…。似乎您不是唯一一个想知道服务定位器和抽象工厂模式之间有什么不同的人:) 【参考方案1】:

我在调查这些模式时偶然发现了同样的问题。我认为可以在 Service Locator 和 Factory 之间找到主要区别(无论它是否抽象):

服务定位器

“定位”一个现有依赖项(服务)。虽然服务可以在解析期间创建,但对客户没有影响,因为: 服务定位器的客户端不获取依赖项的所有权

工厂

创建一个新的依赖实例。 Factory 的客户获得了依赖项的所有权

抽象工厂

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

【讨论】:

+1;关于目的不同的好点。我认为这通过模式的名称很明显,所以我专注于机制和公共接口,而不是使用意图。就两个类返回的对象的生命周期而言,这种区别非常重要。 我认为意图是一种模式的定义特征。例如,装饰器和代理可以以相同的方式实现,但它们的意图不同。 接受你的回答支持我,因为模式的意图比界面如何支持该意图更重要。 请提供代码示例以支持您所说的。这会让它更容易理解【参考方案2】:

从我目前阅读的内容来看,我认为区别在于:

服务定位器模式

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

抽象工厂模式

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

【讨论】:

我对我的回答并不肯定,因为我对服务定位器模式没有太多经验。如果有人想提供更明智的答案,请随意。 我现在对这些概念感到更加自在。我的答案是正确的,但它是故事的一半。意图更重要。请参阅该问题的其他答案 - ***.com/a/9403827/232593 (另外:我建议不要使用 DI 容器,除非实现插件架构。在您的应用程序边界内使用手动构造函数注入,在您需要它们的工厂中使用它们。在 main 中构建对象树,或拥有“模块”。使用服务定位器连接到真正不可靠的“服务”,而不是您的程序将拥有并完全控制确保存在的进程/系统。【参考方案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 方法的返回类型由工厂本身的类型决定。也就是说,构造类型只能返回单一类型的实例。”

虽然 Rober C. Martin 没有提及任何关于泛型类型的内容,此外,他书中的工厂示例允许创建多个类型对象的实例,使用密钥字符串作为 Factory.Make( )。

Gamma 说抽象工厂的目的是“提供一个接口来创建相关或依赖对象的系列,而无需指定它们的具体类”。值得一提的是,Gamma Abstract Factory 示例违反了 Martin 所说的接口隔离原则 (ISP)。 ISP 和 SOLID 通常是更现代的原则,或者为了简单起见,省略了。

Gamma 和 Martin 的作品先于 Seemann,所以我认为他应该遵循已经做出的定义。

虽然 Fowler 提出服务定位器作为实现依赖倒置的一种方式,但 Seemann 认为它是一种反模式。 Gamma 或 Martin 都没有提到服务定位器。

但是,Seemann 和 Fowler 一致认为,Service Locator 需要一个配置步骤来注册具体类的实例,该实例将在稍后请求此类对象时返回。 Martin 或 Gamma 在他们对抽象工厂的定义中没有提到这个配置步骤。抽象工厂模式假设每次请求此类对象时都会实例化一个新对象。

结论

服务定位器和抽象工厂之间的主要区别在于,抽象工厂假设在每次请求时都会实例化一个新对象并返回,并且服务定位器需要配置一个对象实例,并且每次都将返回相同的实例。

【讨论】:

我同意 Seemann 的抽象工厂概念不符合 GoF 的定义;但是为什么你说GoF的定义违反了ISP?目的是让每个客户都使用每个工厂方法。【参考方案4】:

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

抽象工厂是泛型类型,Create方法的返回类型由工厂本身的类型决定。换句话说,构造类型只能返回单一类型的实例。

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

【讨论】:

【参考方案5】:

Martin Fowler 描述了Service Locator Pattern 的几个实现,其中大部分是具体类,通过static 方法配置和调用。我认为我们可以忽略这些变化并专注于他使用依赖注入的最后一个示例,这是一种更现代的方法。我们可以将其与 GoF book 中的抽象工厂模式进行比较。

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

考虑到抽象工厂和服务定位器之间的选择,前一种模式更可取。但这是一种错误的二分法,因为有更多选择。这两种模式都依赖于依赖注入,但没有它们,DI 会更好地工作。

注入是一种实现控制反转的机制。 IoC 是依赖倒置的理想形式,因为它最大限度地减少了客户端的复杂性并提供了***别的抽象。服务定位器和抽象工厂是实现依赖反转没有控制反转的两种机制。从 IoC 是理想的角度来看,它们都是不可取的,而且它们都没有实现。

有关 IoC 与 DI 的更多信息,请参阅:Inversion of Control vs Dependency Injection。 有关抽象工厂和服务定位器陷阱的更多信息,请参阅:Dependency Injection Code Smell。

【讨论】:

以上是关于服务定位器模式与抽象工厂模式有啥不同吗?的主要内容,如果未能解决你的问题,请参考以下文章

工厂模式与抽象工厂模式

设计模式与Android抽象工厂模式——嵌合体克隆工厂

PHP设计模式之工厂模式

就不能换DB吗? 抽象工厂模式

抽象工厂模式

工厂模式与抽象工厂的区别