--- title: 设计模式之抽象工厂模式 date: 2015-06-03 10:26:00 order: 03 categories: - 设计 - 设计模式 tags: - 设计 - 设计模式 permalink: /pages/5b12dab7/ --- # 设计模式之抽象工厂模式 ## 意图 **抽象工厂模式** (Abstract Factory)是一种创建型设计模式, 它能创建一系列相关的对象, 而无需指定其具体类。 **优点 ** - 抽象工厂模式**隔离了具体类的生成**,用户并不需要知道什么被创建。由于这种隔离,更换一个具体工厂变得相对容易。所有的具体工厂都实现了抽象工厂中定义的那些公共接口,因此只需要改变具体工厂的实例,就可以在某种程度上改变整个软件系统的行为。另外,应用抽象工厂模式可以实现高内聚低耦合的设计目的,因此抽象工厂模式得到了广泛的应用。 - 当一个产品族中的多个对象被设计成一起工作时,它**能够保证客户端始终只使用同一个产品族中的对象**。这对一些需要根据当前环境来决定其行为的软件系统来说,是一种非常实用的设计模式。 - **增加新的具体工厂和产品族很方便,无须修改已有系统,符合“开放封闭原则”**。 **缺点** - 在添加新的产品对象时,难以扩展抽象工厂来生产新种类的产品,这是因为在抽象工厂角色中规定了所有可能被创建的产品集合,要支持新种类的产品就意味着要对该接口进行扩展,而这将涉及到对抽象工厂角色及其所有子类的修改,显然会带来较大的不便。 ## 适用场景 抽象工厂模式适用场景: 一个系统要独立于它的产品的创建、组合和表示时。 一个系统要由多个产品系列中的一个来配置时。 当你要强调一系列相关的产品对象的设计以便进行联合使用时。 当你提供一个产品类库,而只想显示它们的接口而不是实现时。 ## 结构 ![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20210517195501.png) ### 结构说明 1. **抽象产品** (Abstract Product) 为构成系列产品的一组不同但相关的产品声明接口。 2. **具体产品** (Concrete Product) 是抽象产品的多种不同类型实现。 所有变体 (维多利亚/现代) 都必须实现相应的抽象产品 (椅子/沙发)。 3. **抽象工厂** (Abstract Factory) 接口声明了一组创建各种抽象产品的方法。 4. **具体工厂** (Concrete Factory) 实现抽象工厂的构建方法。 每个具体工厂都对应特定产品变体, 且仅创建此种产品变体。 5. 尽管具体工厂会对具体产品进行初始化, 其构建方法签名必须返回相应的*抽象*产品。 这样, 使用工厂类的客户端代码就不会与工厂创建的特定产品变体耦合。 **客户端** (Client) 只需通过抽象接口调用工厂和产品对象, 就能与任何具体工厂/产品变体交互。 ### 结构代码范式 【AbstractProduct】 声明一个接口,这个接口中包含产品对象类型。 ```java abstract class AbstractProductA { public abstract void show(); } abstract class AbstractProductB { public abstract void show(); } ``` 【ConcreteProduct】 定义一个产品对象,这个产品对象是由相关的具体工厂创建的。 ```java class ConcreteProductA1 extends AbstractProductA {     @Override     public void show() {         System.out.println("ConcreteProductA1");     } } class ConcreteProductA2 extends AbstractProductA {     @Override     public void show() {         System.out.println("ConcreteProductA2");     } } class ConcreteProductB1 extends AbstractProductB {     @Override     public void show() {         System.out.println("ConcreteProductB1");     } } class ConcreteProductB2 extends AbstractProductB {     @Override     public void show() {         System.out.println("ConcreteProductB2");     } } ``` 【AbstractFactory】 声明一个接口,这个接口中包含创建抽象产品对象的方法。 ```java abstract class AbstractFactory { public abstract AbstractProductA createProductA(); public abstract AbstractProductB createProductB(); } ``` 【ConcreteFactory】 实现创建具体产品对象的方法。 ```java class ConcreteFactory1 extends AbstractFactory { @Override public AbstractProductA createProductA() { return new ConcreteProductA1(); } @Override public AbstractProductB createProductB() { return new ConcreteProductB1(); } } class ConcreteFactory2 extends AbstractFactory { @Override public AbstractProductA createProductA() { return new ConcreteProductA2(); } @Override public AbstractProductB createProductB() { return new ConcreteProductB2(); } } ``` 【客户端】 只使用 `AbstractFactory` 和 `AbstractProduct` 声明的接口。 ```java public class AbstarctFactoryPattern { public static void main(String[] args) { AbstractFactory factory1 = new ConcreteFactory1(); AbstractProductA productA1 = factory1.createProductA(); AbstractProductB productB1 = factory1.createProductB(); productA1.show(); productB1.show(); AbstractFactory factory2 = new ConcreteFactory2(); AbstractProductA productA2 = factory2.createProductA(); AbstractProductB productB2 = factory2.createProductB(); productA2.show(); productB2.show(); } } ``` 【输出】 ``` ConcreteProductA1 ConcreteProductB1 ConcreteProductA2 ConcreteProductB2 ``` ## 伪代码 下面例子通过应用**抽象工厂**模式, 使得客户端代码无需与具体 UI 类耦合, 就能创建跨平台的 UI 元素, 同时确保所创建的元素与指定的操作系统匹配。 ![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20210517195732.png) 跨平台应用中的相同 UI 元素功能类似, 但是在不同操作系统下的外观有一定差异。 此外, 你需要确保 UI 元素与当前操作系统风格一致。 你一定不希望在 Windows 系统下运行的应用程序中显示 macOS 的控件。 抽象工厂接口声明一系列构建方法, 客户端代码可调用它们生成不同风格的 UI 元素。 每个具体工厂对应特定操作系统, 并负责生成符合该操作系统风格的 UI 元素。 其运作方式如下: 应用程序启动后检测当前操作系统。 根据该信息, 应用程序通过与该操作系统对应的类创建工厂对象。 其余代码使用该工厂对象创建 UI 元素。 这样可以避免生成错误类型的元素。 使用这种方法, 客户端代码只需调用抽象接口, 而无需了解具体工厂类和 UI 元素。 此外, 客户端代码还支持未来添加新的工厂或 UI 元素。 这样一来, 每次在应用程序中添加新的 UI 元素变体时, 你都无需修改客户端代码。 你只需创建一个能够生成这些 UI 元素的工厂类, 然后稍微修改应用程序的初始代码, 使其能够选择合适的工厂类即可。 ```java // 抽象工厂接口声明了一组能返回不同抽象产品的方法。这些产品属于同一个系列 // 且在高层主题或概念上具有相关性。同系列的产品通常能相互搭配使用。系列产 // 品可有多个变体,但不同变体的产品不能搭配使用。 interface GUIFactory is method createButton():Button method createCheckbox():Checkbox // 具体工厂可生成属于同一变体的系列产品。工厂会确保其创建的产品能相互搭配 // 使用。具体工厂方法签名会返回一个抽象产品,但在方法内部则会对具体产品进 // 行实例化。 class WinFactory implements GUIFactory is method createButton():Button is return new WinButton() method createCheckbox():Checkbox is return new WinCheckbox() // 每个具体工厂中都会包含一个相应的产品变体。 class MacFactory implements GUIFactory is method createButton():Button is return new MacButton() method createCheckbox():Checkbox is return new MacCheckbox() // 系列产品中的特定产品必须有一个基础接口。所有产品变体都必须实现这个接口。 interface Button is method paint() // 具体产品由相应的具体工厂创建。 class WinButton implements Button is method paint() is // 根据 Windows 样式渲染按钮。 class MacButton implements Button is method paint() is // 根据 macOS 样式渲染按钮 // 这是另一个产品的基础接口。所有产品都可以互动,但是只有相同具体变体的产 // 品之间才能够正确地进行交互。 interface Checkbox is method paint() class WinCheckbox implements Checkbox is method paint() is // 根据 Windows 样式渲染复选框。 class MacCheckbox implements Checkbox is method paint() is // 根据 macOS 样式渲染复选框。 // 客户端代码仅通过抽象类型(GUIFactory、Button 和 Checkbox)使用工厂 // 和产品。这让你无需修改任何工厂或产品子类就能将其传递给客户端代码。 class Application is private field factory: GUIFactory private field button: Button constructor Application(factory: GUIFactory) is this.factory = factory method createUI() is this.button = factory.createButton() method paint() is button.paint() // 程序会根据当前配置或环境设定选择工厂类型,并在运行时创建工厂(通常在初 // 始化阶段)。 class ApplicationConfigurator is method main() is config = readApplicationConfigFile() if (config.OS == "Windows") then factory = new WinFactory() else if (config.OS == "Mac") then factory = new MacFactory() else throw new Exception("错误!未知的操作系统。") Application app = new Application(factory) ``` ## 案例 众所周知,苹果和三星这两家世界级的电子产品厂商都生产手机和电脑。 我们以生产手机和电脑为例,演示一下抽象工厂模式的应用 【AbstractProduct 角色】 首先,定义手机和电脑两个抽象接口,他们都有各自的产品信息。 ```java interface Telephone { public String getProductInfo(); } interface Computer { public String getProductInfo(); } ``` 【ConcreteProduct 角色】 `ConcreteProduct` 根据 `AbstractProduct` 来定义具体的产品属性、方法。 在我们的例子中,苹果、三星两家公司的手机和电脑都有各自的具体产品信息。 ```java class AppleTelephone implements Telephone { @Override public String getProductInfo() { return "苹果手机,采用ios系统"; } } class SamsungTelephone implements Telephone { @Override public String getProductInfo() { return "三星手机,采用android系统"; } } class AppleComputer implements Computer { @Override public String getProductInfo() { return "苹果电脑,采用mac系统"; } } class SamsungComputer implements Computer { @Override public String getProductInfo() { return "三星电脑,采用windows系统"; } } ``` 【AbstractFactory 角色】 苹果,三星这两个厂商都生产手机和电脑。所以它们可以有一个抽象父类或父接口,提供生产手机和生产电脑的方法。 ```java interface ElectronicFactory { public Telephone produceTelephone(); public Computer produceComputer(); } ``` 【ConcreteFactory 角色】 苹果、三星工厂分别实现父接口,生产不同类型的产品。 ```java class AppleFactory implements ElectronicFactory { @Override public Telephone produceTelephone() { return new AppleTelephone(); } @Override public Computer produceComputer() { return new AppleComputer(); } } class SamsungFactory implements ElectronicFactory { @Override public Telephone produceTelephone() { return new SamsungTelephone(); } @Override public Computer produceComputer() { return new SamsungComputer(); } } ``` 【客户端】 ```java public class PhoneFactoryDemo { public static void main(String[] args) { ElectronicFactory appleFactory = new AppleFactory(); Telephone phone = appleFactory.produceTelephone(); System.out.println(phone.getProductInfo()); Computer computer = appleFactory.produceComputer(); System.out.println(computer.getProductInfo()); } } ``` 【输出】 ``` 苹果手机,采用ios系统 苹果电脑,采用mac系统 ``` ## 与其他模式的关系 - 在许多设计工作的初期都会使用[工厂方法模式](https://refactoringguru.cn/design-patterns/factory-method) (较为简单, 而且可以更方便地通过子类进行定制), 随后演化为使用[抽象工厂模式](https://refactoringguru.cn/design-patterns/abstract-factory)、 [原型模式](https://refactoringguru.cn/design-patterns/prototype)或[生成器模式](https://refactoringguru.cn/design-patterns/builder) (更灵活但更加复杂)。 - [生成器](https://refactoringguru.cn/design-patterns/builder)重点关注如何分步生成复杂对象。 [抽象工厂](https://refactoringguru.cn/design-patterns/abstract-factory)专门用于生产一系列相关对象。 *抽象工厂*会马上返回产品, *生成器*则允许你在获取产品前执行一些额外构造步骤。 - [抽象工厂模式](https://refactoringguru.cn/design-patterns/abstract-factory)通常基于一组[工厂方法](https://refactoringguru.cn/design-patterns/factory-method), 但你也可以使用[原型模式](https://refactoringguru.cn/design-patterns/prototype)来生成这些类的方法。 - 当只需对客户端代码隐藏子系统创建对象的方式时, 你可以使用[抽象工厂](https://refactoringguru.cn/design-patterns/abstract-factory)来代替[外观模式](https://refactoringguru.cn/design-patterns/facade)。 - 你可以将[抽象工厂](https://refactoringguru.cn/design-patterns/abstract-factory)和[桥接模式](https://refactoringguru.cn/design-patterns/bridge)搭配使用。 如果由*桥接*定义的抽象只能与特定实现合作, 这一模式搭配就非常有用。 在这种情况下, *抽象工厂*可以对这些关系进行封装, 并且对客户端代码隐藏其复杂性。 - [抽象工厂](https://refactoringguru.cn/design-patterns/abstract-factory)、 [生成器](https://refactoringguru.cn/design-patterns/builder)和[原型](https://refactoringguru.cn/design-patterns/prototype)都可以用[单例模式](https://refactoringguru.cn/design-patterns/singleton)来实现。 ## 参考资料 - [《Head First 设计模式》](https://book.douban.com/subject/2243615/) - [《大话设计模式》](https://book.douban.com/subject/2334288/) - [设计模式教程](https://refactoringguru.cn/design-patterns/catalog)