01 意图
抽象工厂是一种创建设计模式,它允许您生成相关对象的系列,而无需指定它们的具体类。
02 问题
想象一下,您正在创建一个家具店模拟器。您的代码由代表的类组成:
一系列相关产品,例如:
Chair
++ 。Sofa``CoffeeTable
这个家族的几个变种。例如,产品
Chair
++有以下变体Sofa
:、、。CoffeeTable``Modern``Victorian``ArtDeco
您需要一种方法来创建单个家具对象,以便它们与同一系列的其他对象相匹配。当客户收到不匹配的家具时,他们会非常生气。
[图片上传中...(image-b3c8e2-1642771338019-1)]
此外,在向程序添加新产品或产品系列时,您不希望更改现有代码。家具供应商经常更新他们的目录,您不会希望每次都更改核心代码。
03 解决方案
抽象工厂模式建议的第一件事是为产品系列的每个不同产品(例如,椅子、沙发或咖啡桌)显式声明接口。然后,您可以使所有产品变体都遵循这些接口。例如,所有椅子变体都可以实现该Chair接口;所有咖啡桌变体都可以实现该CoffeeTable接口,依此类推。
下一步是声明抽象工厂——一个接口,其中包含产品系列中所有产品的创建方法列表(例如createChair,createSofa和createCoffeeTable)。这些方法必须返回由我们之前提取的接口表示的抽象产品类型:Chair、Sofa等CoffeeTable
现在,产品变体怎么样?对于产品系列的每个变体,我们基于AbstractFactory
接口创建一个单独的工厂类。工厂是返回特定种类产品的类。例如,ModernFurnitureFactory
只能创建ModernChair
,ModernSofa
和ModernCoffeeTable
对象。
客户端代码必须通过它们各自的抽象接口与工厂和产品一起工作。这使您可以更改传递给客户端代码的工厂类型以及客户端代码接收的产品变体,而不会破坏实际的客户端代码。
[图片上传中...(image-c207b-1642771338018-0)]
假设客户想要一家工厂生产一把椅子。客户不必知道工厂的等级,也不必关心它得到什么样的椅子。无论是现代模型还是维多利亚风格的椅子,客户都必须使用抽象Chair
接口以相同的方式对待所有椅子。使用这种方法,客户对椅子的唯一了解就是它sitOn
以某种方式实现了该方法。此外,无论返回哪种椅子变体,它都将始终与同一工厂对象生产的沙发或咖啡桌类型相匹配。
还有一件事需要澄清:如果客户端只暴露给抽象接口,那么是什么创建了实际的工厂对象?通常,应用程序在初始化阶段创建一个具体的工厂对象。在此之前,应用程序必须根据配置或环境设置选择工厂类型。
04 结构实现
此示例说明了如何使用抽象工厂模式来创建跨平台 UI 元素,而无需将客户端代码耦合到具体的 UI 类,同时使所有创建的元素与所选操作系统保持一致。
跨平台应用程序中的相同 UI 元素应具有相似的行为,但在不同的操作系统下看起来会有所不同。此外,确保 UI 元素与当前操作系统的风格相匹配是您的工作。您不希望您的程序在 Windows 中执行时呈现 macOS 控件。
抽象工厂接口声明了一组创建方法,客户端代码可以使用这些方法来生成不同类型的 UI 元素。具体工厂对应于特定的操作系统,并创建与该特定操作系统匹配的 UI 元素。
它的工作原理是这样的:当应用程序启动时,它会检查当前操作系统的类型。应用程序使用此信息从与操作系统匹配的类中创建工厂对象。其余代码使用这个工厂来创建 UI 元素。这可以防止创建错误的元素。
使用这种方法,客户端代码不依赖于工厂和 UI 元素的具体类,只要它通过它们的抽象接口与这些对象一起工作。这也让客户端代码支持您将来可能添加的其他工厂或 UI 元素。
因此,您无需在每次向应用程序添加新的 UI 元素变体时修改客户端代码。您只需创建一个新的工厂类来生成这些元素并稍微修改应用程序的初始化代码,以便它在适当时选择该类。
// The abstract factory interface declares a set of methods that
05 适用场景
当您的代码需要与各种相关产品系列一起使用时,请使用抽象工厂,但您不希望它依赖于这些产品的具体类——它们可能事先是未知的,或者您只是想考虑到未来的可扩展性。
抽象工厂为您提供了一个接口,用于从产品系列的每个类创建对象。只要您的代码通过此接口创建对象,您就不必担心创建与您的应用程序已创建的产品不匹配的产品的错误变体。
当您的类具有一组模糊其主要职责的工厂方法时,请考虑实现抽象工厂。
在精心设计的程序中,每个班级只负责一件事。当一个类处理多种产品类型时,可能值得将其工厂方法提取到独立的工厂类或成熟的抽象工厂实现中。
06 如何实施
绘制出不同产品类型与这些产品变体的矩阵。
为所有产品类型声明抽象产品接口。然后让所有具体的产品类实现这些接口。
用一组所有抽象产品的创建方法声明抽象工厂接口。
实现一组具体的工厂类,每个产品变体一个。
在应用程序的某处创建工厂初始化代码。它应该实例化具体工厂类之一,具体取决于应用程序配置或当前环境。将此工厂对象传递给构造产品的所有类。
扫描代码并找到对产品构造函数的所有直接调用。将它们替换为对工厂对象的适当创建方法的调用。
07 优缺点