设计模式系列往期文章
- 设计模式学习之策略模式
- 设计模式学习之策略模式在前端的应用
- 设计模式学习之简单工厂模式
- 设计模式学习之工厂方法模式
如果你已经理解了工厂方法模式,那你能够很快的明白抽象工厂模式。
温习:什么是工厂方法模式
我们先温习一下什么是工厂方法模式——工厂方法模式定义了一个工厂类接口,基于这个接口实现多个具体的工厂类,每个工厂类负责生产一种产品,类图如下图所示:
问题引入:什么是抽象工厂模式
那么,什么是抽象工厂模式呢,我们可以把工厂方法模式看做是极端情况下的抽象工厂模式——即工厂类只生产一类产品,如果每个工厂类能够生产多类产品,这样的工厂模式就是抽象工厂模式。举个栗子,假设某个家具工厂能够生产椅子、沙发、咖啡桌等一系列家具,每种家具有有多种风格(如装饰风、维多利亚风、现代风格等),用户肯定希望买一套相同风格的家具而不是混搭,因此可以设置多个生产线,每个生产线只生产一种风格的家具,如下图所示:
因此家具生产这个工厂类就可能设计成下面的样子,每个工厂能够生产椅子、咖啡桌和沙发:
在上面这种应用场景中,我们就需要使用抽象工厂模式,其主要特点就是:工厂能够生产多种产品,同一个工厂生产出来的产品应该具有类似的风格/应用场景(正如上面的家具的例子)。类图结构如下图所示:
代码演示
我们继续扩展设计模式学习之工厂方法模式一文中的例子,由于环境不同,在Windows上和网页端或者MacOS系统中绘制GUI界面的代码实现肯定会有一些差别,而且工厂生产出来的组件肯定不止Button一种类型,在真实的应用场景中还会有输入框、单选框、多选框、下拉框等等组件。这个需求是不是正是抽象工厂模式的应用场景?于是可以设计出如下的类图(其实每个工厂类需要创建的产品不止两个,下图仅是示例):
基于类图给出如下伪代码:
// 抽象工厂接口声明了一组能返回不同抽象产品的方法。这些产品属于同一个系列
// 且在高层主题或概念上具有相关性。同系列的产品通常能相互搭配使用。系列产
// 品可有多个变体,但不同变体的产品不能搭配使用。
interface GUIFactory ismethod createButton():Buttonmethod createCheckbox():Checkbox// 具体工厂可生成属于同一变体的系列产品。工厂会确保其创建的产品能相互搭配
// 使用。具体工厂方法签名会返回一个抽象产品,但在方法内部则会对具体产品进
// 行实例化。
class WinFactory implements GUIFactory ismethod createButton():Button isreturn new WinButton()method createCheckbox():Checkbox isreturn new WinCheckbox()// 每个具体工厂中都会包含一个相应的产品变体。
class MacFactory implements GUIFactory ismethod createButton():Button isreturn new MacButton()method createCheckbox():Checkbox isreturn new MacCheckbox()// 系列产品中的特定产品必须有一个基础接口。所有产品变体都必须实现这个接口。
interface Button ismethod paint()// 具体产品由相应的具体工厂创建。
class WinButton implements Button ismethod paint() is// 根据 Windows 样式渲染按钮。class MacButton implements Button ismethod paint() is// 根据 macOS 样式渲染按钮// 这是另一个产品的基础接口。所有产品都可以互动,但是只有相同具体变体的产
// 品之间才能够正确地进行交互。
interface Checkbox ismethod paint()class WinCheckbox implements Checkbox ismethod paint() is// 根据 Windows 样式渲染复选框。class MacCheckbox implements Checkbox ismethod paint() is// 根据 macOS 样式渲染复选框。// 客户端代码仅通过抽象类型(GUIFactory、Button 和 Checkbox)使用工厂
// 和产品。这让你无需修改任何工厂或产品子类就能将其传递给客户端代码。
class Application isprivate field factory: GUIFactoryprivate field button: Buttonconstructor Application(factory: GUIFactory) isthis.factory = factorymethod createUI() isthis.button = factory.createButton()method paint() isbutton.paint()// 程序会根据当前配置或环境设定选择工厂类型,并在运行时创建工厂(通常在初
// 始化阶段)。
class ApplicationConfigurator ismethod main() isconfig = readApplicationConfigFile()if (config.OS == "Windows") thenfactory = new WinFactory()else if (config.OS == "Mac") thenfactory = new MacFactory()elsethrow new Exception("错误!未知的操作系统。")Application app = new Application(factory)
说明:对比一下工厂方法模式和抽象工厂模式,你会发现代码结构几乎一致,只是GUIFactory
中创建产品的方法变多了。
类图
抽象工厂模式的类图如下图所示:
对应的plantUML绘图代码为:
@startumlskinparam linetype orthopackage "Abstract Factory" <<Frame>> {interface ProductAIntf {+ method()}interface ProductBIntf {+ method()}interface FactoryIntf {+ createProductA()+ createProductB()}class ConcreteProductA1 {+ method()}class ConcreteProductA2 {+ method()}class ConcreteProductB1 {+ method()}class ConcreteProductB2 {+ method()}class ConcereteFactoryA {+ createProductA()+ createProductB()}class ConcereteFactoryB {+ createProductA()+ createProductB()}ProductAIntf <|.. ConcreteProductA1ProductAIntf <|.. ConcreteProductA2ProductBIntf <|.. ConcreteProductB1ProductBIntf <|.. ConcreteProductB2FactoryIntf <|.. ConcereteFactoryAFactoryIntf <|.. ConcereteFactoryBConcreteProductA1 <.. ConcereteFactoryAConcreteProductA2 <.. ConcereteFactoryAConcreteProductB1 <.. ConcereteFactoryBConcreteProductB2 <.. ConcereteFactoryB}
@enduml