抽象工厂(Abstract Factory)
抽象工厂是一种创建型设计模式, 它能创建一系列相关的对象,而 无需指定其具体类。
案例说明
跨平台应用中的相同 UI 元素功能类似,但是在不同操作系统下的外观有一定差异。此外,你需要确保 UI 元素与当前操
作系统风格一致。你一定不希望在 Windows 系统下运行的应用程序中显示 macOS 的控件。抽象工厂接口声明一系列构建方法,
客户端代码可调用它们生成不同风格的 UI 元素。每个具体工厂对应特定操作系统,并负责生成符合该操作系统风格的 UI 元素。
UML图
- 抽象产品(Abstract Product)为构成系列产品的一组不同但 相关的产品声明接口。
- 具体产品(Concrete Product)是抽象产品的多种不同类型实 现。所有变体(维多利亚/现代)都必须实现相应的抽象产品 (椅子/沙发)。
- 抽象工厂(Abstract Factory)接口声明了一组创建各种抽象 产品的方法。
- 具体工厂(Concrete Factory)实现抽象工厂的构建方法。每 个具体工厂都对应特定产品变体,且仅创建此种产品变体。
- 尽管具体工厂会对具体产品进行初始化,其构建方法签名必 须返回相应的抽象产品。这样,使用工厂类的客户端代码就 不会与工厂创建的特定产品变体耦合。客户端(Client)只 需通过抽象接口调用工厂和产品对象,就能与任何具体工厂/ 产品变体交互。
核心代码
factory/Factory
/**
* @author: ccyy
* @create: 2021-10-12
* @description: 抽象工厂接口声明了一组能返回不同抽象产品
* 的方法。这些产品属于同一个系列且在高层主题或概念上具
* 有相关性。 同系列的产品通常能相互搭配使用。
* 系列产品可有多个变体,但不同变体的产品不能搭配使用。
**/
public interface GUIFactory {
Button createButton();
Checkbox createCheckbox();
}
factory/MacFactory
/**
* @author: ccyy
* @create: 2021-10-12
* @description:
**/
public class MacFactory implements GUIFactory{
@Override
public Button createButton() {
return new MacButton();
}
@Override
public Checkbox createCheckbox() {
return new MacCheckbox();
}
}
factory/WinFactory
/**
* @author: ccyy
* @create: 2021-10-12
* @description: 具体工厂可生成属于同一变体的系列产品。
* 工厂会确保其创建的产品能相互搭配使用。
* 具体工厂方法签名会返回一个抽象产品,
* 但在方法内部则会对具体产品进行实例化。
**/
public class WinFactory implements GUIFactory{
@Override
public Button createButton() {
return new WinButton();
}
@Override
public Checkbox createCheckbox() {
return new WinCheckbox();
}
}
project/Button
/**
* @author: ccyy
* @create: 2021-10-12
* @description: 系列产品中的特定产品必须有一个基础接口。
* 所有产品变体都必须实现这个接口。
**/
public interface Button {
void paint();
}
project/Checkbox
/**
* @author: ccyy
* @create: 2021-10-12
* @description: 这是另一个产品的基础接口。
* 所有产品都可以互动,
* 但是只有相同具体变体的产品之间才能够正确地进行交互。
**/
public interface Checkbox {
void paint();
}
project/mac/MacButton
/**
* @author: ccyy
* @create: 2021-10-12
* @description: mac按钮样式
**/
public class MacButton implements Button {
@Override
public void paint() {
System.out.println("---创建mac按钮");
}
}
project/mac/MacCheckbox
/**
* @author: ccyy
* @create: 2021-10-12
* @description: mac选择框样式
**/
public class MacCheckbox implements Checkbox {
@Override
public void paint() {
System.out.println("---创建mac选择框");
}
}
project/win/WinButton
/**
* @author: ccyy
* @create: 2021-10-12
* @description: window按钮样式
**/
public class WinButton implements Button {
/**
* 根据 Windows 样式渲染按钮。
*/
@Override
public void paint() {
System.out.println("---创建windows按钮");
}
}
project/mac/WinCheckbox
/**
* @author: ccyy
* @create: 2021-10-12
* @description: windows选择框样式
**/
public class WinCheckbox implements Checkbox {
@Override
public void paint() {
System.out.println("---创建windows选择框");
}
}
Application
/**
* @author: ccyy
* @create: 2021-10-12
* @description: 客户端代码仅通过抽象类型
* (GUIFactory、Button 和 Checkbox)使用工厂和产品。
* 这让你无需修改任何工厂或产品子类就能将其传递给客户端代码。
**/
public class Application {
private GUIFactory factory;
public Application(GUIFactory factory) {
this.factory = factory;
}
public void paint(){
Button button = factory.createButton();
Checkbox checkbox = factory.createCheckbox();
button.paint();
checkbox.paint();
}
}
Main
/**
* @author: ccyy
* @create: 2021-10-12
* @description: 测试
**/
public class Main {
public static void main(String[] args) {
// 创建windows组件
System.out.println("-----创建windows");
Application win = new Application(new WinFactory());
win.paint();
// 创建mac组件
System.out.println("-----创建mac");
Application mac = new Application(new MacFactory());
mac.paint();
}
}
适用场景
如果代码需要与多个不同系列的相关产品交互,但是由于无 法提前获取相关信息,或者出于对未来扩展性的考虑,你不 希望代码基于产品的具体类进行构建,在这种情况下,你可 以使用抽象工厂。
如果你有一个基于一组抽象方法的类,且其主要功能因此变 得不明确,那么在这种情况下可以考虑使用抽象工厂模式。
优缺点
优点:
- 你可以确保同一工厂生成的产品相互匹配。
- 你可以避免客户端和具体产品代码的耦合。 单一职责原则。
- 你可以将产品生成代码抽取到同一位置,使 得代码易于维护。
- 开闭原则。向应用程序中引入新产品变体时,你无需修改客 户端代码。
缺点
由于采用该模式需要向应用中引入众多接口和类,代码可能 会比之前更加复杂。
与其他模式的关系
- 在许多设计工作的初期都会使用工厂方法(较为简单,而且 可以更方便地通过子类进行定制),随后演化为使用抽象工 厂、原型或生成器(更灵活但更加复杂)。
- 生成器重点关注如何分步生成复杂对象。抽象工厂专门用于 生产一系列相关对象。抽象工厂会马上返回产品,生成器则 允许你在获取产品前执行一些额外构造步骤。
- 抽象工厂模式通常基于一组工厂方法,但你也可以使用原型 模式来生成这些类的方法
- 当只需对客户端代码隐藏子系统创建对象的方式时,你可以 使用抽象工厂来代替外观。
- 你可以将抽象工厂和桥接搭配使用。如果由桥接定义的抽象 只能与特定实现合作,这一模式搭配就非常有用。在这种情 况下,抽象工厂可以对这些关系进行封装,并且对客户端代 码隐藏其复杂性。
- 抽象工厂、生成器和原型都可以用单例来实现。