设计模式之抽象工厂模式
设计模式之抽象工厂模式
意图
抽象工厂模式 (Abstract Factory)是一种创建型设计模式, 它能创建一系列相关的对象, 而无需指定其具体类。
**优点 **
抽象工厂模式隔离了具体类的生成,用户并不需要知道什么被创建。由于这种隔离,更换一个具体工厂变得相对容易。所有的具体工厂都实现了抽象工厂中定义的那些公共接口,因此只需要改变具体工厂的实例,就可以在某种程度上改变整个软件系统的行为。另外,应用抽象工厂模式可以实现高内聚低耦合的设计目的,因此抽象工厂模式得到了广泛的应用。
当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象。这对一些需要根据当前环境来决定其行为的软件系统来说,是一种非常实用的设计模式。
增加新的具体工厂和产品族很方便,无须修改已有系统,符合“开放封闭原则”。
缺点
- 在添加新的产品对象时,难以扩展抽象工厂来生产新种类的产品,这是因为在抽象工厂角色中规定了所有可能被创建的产品集合,要支持新种类的产品就意味着要对该接口进行扩展,而这将涉及到对抽象工厂角色及其所有子类的修改,显然会带来较大的不便。
适用场景
抽象工厂模式适用场景:
一个系统要独立于它的产品的创建、组合和表示时。
一个系统要由多个产品系列中的一个来配置时。
当你要强调一系列相关的产品对象的设计以便进行联合使用时。
当你提供一个产品类库,而只想显示它们的接口而不是实现时。
结构
结构说明
- 抽象产品 (Abstract Product) 为构成系列产品的一组不同但相关的产品声明接口。
- 具体产品 (Concrete Product) 是抽象产品的多种不同类型实现。 所有变体 (维多利亚/现代) 都必须实现相应的抽象产品 (椅子/沙发)。
- 抽象工厂 (Abstract Factory) 接口声明了一组创建各种抽象产品的方法。
- 具体工厂 (Concrete Factory) 实现抽象工厂的构建方法。 每个具体工厂都对应特定产品变体, 且仅创建此种产品变体。
- 尽管具体工厂会对具体产品进行初始化, 其构建方法签名必须返回相应的抽象产品。 这样, 使用工厂类的客户端代码就不会与工厂创建的特定产品变体耦合。 客户端 (Client) 只需通过抽象接口调用工厂和产品对象, 就能与任何具体工厂/产品变体交互。
结构代码范式
【AbstractProduct】
声明一个接口,这个接口中包含产品对象类型。
1 | abstract class AbstractProductA { |
【ConcreteProduct】
定义一个产品对象,这个产品对象是由相关的具体工厂创建的。
1 | class ConcreteProductA1 extends AbstractProductA { |
【AbstractFactory】
声明一个接口,这个接口中包含创建抽象产品对象的方法。
1 | abstract class AbstractFactory { |
【ConcreteFactory】
实现创建具体产品对象的方法。
1 | class ConcreteFactory1 extends AbstractFactory { |
【客户端】
只使用 AbstractFactory
和 AbstractProduct
声明的接口。
1 | public class AbstarctFactoryPattern { |
【输出】
1 | ConcreteProductA1 |
伪代码
下面例子通过应用抽象工厂模式, 使得客户端代码无需与具体 UI 类耦合, 就能创建跨平台的 UI 元素, 同时确保所创建的元素与指定的操作系统匹配。
跨平台应用中的相同 UI 元素功能类似, 但是在不同操作系统下的外观有一定差异。 此外, 你需要确保 UI 元素与当前操作系统风格一致。 你一定不希望在 Windows 系统下运行的应用程序中显示 macOS 的控件。
抽象工厂接口声明一系列构建方法, 客户端代码可调用它们生成不同风格的 UI 元素。 每个具体工厂对应特定操作系统, 并负责生成符合该操作系统风格的 UI 元素。
其运作方式如下: 应用程序启动后检测当前操作系统。 根据该信息, 应用程序通过与该操作系统对应的类创建工厂对象。 其余代码使用该工厂对象创建 UI 元素。 这样可以避免生成错误类型的元素。
使用这种方法, 客户端代码只需调用抽象接口, 而无需了解具体工厂类和 UI 元素。 此外, 客户端代码还支持未来添加新的工厂或 UI 元素。
这样一来, 每次在应用程序中添加新的 UI 元素变体时, 你都无需修改客户端代码。 你只需创建一个能够生成这些 UI 元素的工厂类, 然后稍微修改应用程序的初始代码, 使其能够选择合适的工厂类即可。
1 | // 抽象工厂接口声明了一组能返回不同抽象产品的方法。这些产品属于同一个系列 |
案例
众所周知,苹果和三星这两家世界级的电子产品厂商都生产手机和电脑。
我们以生产手机和电脑为例,演示一下抽象工厂模式的应用
【AbstractProduct 角色】
首先,定义手机和电脑两个抽象接口,他们都有各自的产品信息。
1 | interface Telephone { |
【ConcreteProduct 角色】
ConcreteProduct
根据 AbstractProduct
来定义具体的产品属性、方法。
在我们的例子中,苹果、三星两家公司的手机和电脑都有各自的具体产品信息。
1 | class AppleTelephone implements Telephone { |
【AbstractFactory 角色】
苹果,三星这两个厂商都生产手机和电脑。所以它们可以有一个抽象父类或父接口,提供生产手机和生产电脑的方法。
1 | interface ElectronicFactory { |
【ConcreteFactory 角色】
苹果、三星工厂分别实现父接口,生产不同类型的产品。
1 | class AppleFactory implements ElectronicFactory { |
【客户端】
1 | public class PhoneFactoryDemo { |
【输出】
1 | 苹果手机,采用ios系统 |
与其他模式的关系
- 在许多设计工作的初期都会使用工厂方法模式 (较为简单, 而且可以更方便地通过子类进行定制), 随后演化为使用抽象工厂模式、 原型模式或生成器模式 (更灵活但更加复杂)。
- 生成器重点关注如何分步生成复杂对象。 抽象工厂专门用于生产一系列相关对象。 抽象工厂会马上返回产品, 生成器则允许你在获取产品前执行一些额外构造步骤。
- 抽象工厂模式通常基于一组工厂方法, 但你也可以使用原型模式来生成这些类的方法。
- 当只需对客户端代码隐藏子系统创建对象的方式时, 你可以使用抽象工厂来代替外观模式。
- 你可以将抽象工厂和桥接模式搭配使用。 如果由桥接定义的抽象只能与特定实现合作, 这一模式搭配就非常有用。 在这种情况下, 抽象工厂可以对这些关系进行封装, 并且对客户端代码隐藏其复杂性。
- 抽象工厂、 生成器和原型都可以用单例模式来实现。