抽象工厂模式(Abstract Factory Pattern)

前言

工厂方法管单品,抽象工厂管配套。它是保证“产品族”生态一致性的标准解法,能有效杜绝组件混搭。

参考博客:抽象工厂设计模式


一、核心定义

抽象工厂模式是一种创建型设计模式,它提供一个接口,用于创建一系列相关或相互依赖的对象,而无需指定它们的具体类。

与工厂方法模式的核心区别

  • 工厂方法:一个工厂创建一种产品(如 createButton()
  • 抽象工厂:一个工厂创建一族产品(如 createButton() + createCheckbox()
关键词 解释
产品族 同一平台/风格下的一组产品,如 Windows 按钮 + Windows 复选框
产品等级 同一类型产品的不同实现,如 Windows 按钮 vs MacOS 按钮
族内一致性 抽象工厂保证同一工厂生产的产品一定是同一风格,不会出现 Windows 按钮配 MacOS 复选框

二、标准体系结构图(UML)

image-20260415165243353

四个角色:抽象工厂、具体工厂、抽象产品、具体产品。客户端只依赖两层抽象。


三、场景推演:从“单品生产”到“生态全家桶”

在工厂方法模式中,我们有专门生产手机的工厂。但如果业务扩展到智能手表,客户端可能会写出这种代码:

  • 从“苹果工厂”拿了 iPhone
  • 从“小米工厂”拿了 小米手表

由于不同品牌协议不通,iPhone 无法连接小米手表,导致逻辑崩溃。我们需要一种方式,确保客户拿到的必须是同品牌的一整套产品(产品族)

核心思想:一个工厂必须能同时生产一套相关的生态产品。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 1. 定义产品族规范
public interface AbstractFactory {
Phone createPhone(); // 产线1:手机
Watch createWatch(); // 产线2:手表
}

// 2. 具体的品牌全能代工厂
public class AppleFactory implements AbstractFactory {
@Override
public Phone createPhone() { return new IPhone(); }
@Override
public Watch createWatch() { return new AppleWatch(); }
}

public class HuaweiFactory implements AbstractFactory {
@Override
public Phone createPhone() { return new HuaweiPhone(); }
@Override
public Watch createWatch() { return new HuaweiWatch(); }
}

客户端调用:

1
2
3
4
// 客户选定【苹果工厂】,产出的全系产品绝对匹配
AbstractFactory factory = new AppleFactory();
Phone phone = factory.createPhone(); // 拿到 iPhone
Watch watch = factory.createWatch(); // 拿到 AppleWatch

简言之:抽象工厂

  • 核心逻辑:强制约束具体工厂必须提供整套配套产品
  • 巨大优势:保证了产品族的一致性。从架构层面杜绝了“iPhone 配华为手表”这种逻辑错乱。
  • 致命缺陷扩展产品线(增加产品等级)极难。如果要增加“平板电脑”产线,需要修改最顶层接口,导致所有子工厂全部被迫修改,违背开闭原则。

💡 快速区分:

工厂方法:解决“如何造手机”的扩展性(加个华为手机工厂)。

抽象工厂:解决“如何造一整套手机+手表”的配套性(保证全是华为全家桶)。


四、实战案例:跨平台对话框按钮

4.1 需求分析

业务场景:开发一个跨平台 GUI 框架,需要根据操作系统渲染不同风格的 按钮(Button)复选框(Checkbox)

平台 按钮 复选框
Windows Windowsbutton WindowsCheckbox
MacOS MacOSButton MacOSCheckbox

核心约束:同一个应用中,按钮和复选框必须是同一平台风格,不能出现 Windows 按钮 + MacOS 复选框的混搭。

代码结构

1
2
3
4
5
6
7
8
9
10
11
12
13
com.likerhood.design
├── buttons/ # 产品族A - 按钮
│ ├── Button.java # 抽象产品接口
│ ├── MacOSButton.java # MacOS 按钮
│ └── Windowsbutton.java # Windows 按钮
├── checkboxes/ # 产品族B - 复选框
│ ├── Checkbox.java # 抽象产品接口
│ ├── MacOSCheckbox.java # MacOS 复选框
│ └── WindowsCheckbox.java # Windows 复选框
├── factories/ # 工厂层
│ ├── GUIFactory.java # 抽象工厂接口
│ ├── MacOSFactory.java # MacOS 工厂
│ └── WindowsFactory.java # Windows 工厂

代码仓库: https://github.com/likerhood/CodeDesignWork#

4.2 架构图

4.2.1 面条代码架构图

image-20260415211435268

4.2.2 抽象工厂架构图

image-20260415210953358

4.3 类图对比

4.3.1 面条代码类图

Application 扇形依赖全部 4 个具体类,产品之间也没有接口约束。

4.3.2 抽象工厂类图

image-20260415205342886

Application 只依赖 GUIFactoryButtonCheckbox 三个抽象,完全不知道具体类的存在。

4.4 时序图

4.4.1 面条代码时序图

4.4.2 抽象工厂时序图

4.5 代码分析

4.5.1 抽象工厂代码

抽象产品接口

1
2
3
4
5
6
7
8
9
// 按钮接口
public interface Button {
void paint();
}

// 复选框接口
public interface Checkbox {
void paint();
}

具体产品

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// ---- Windows 产品族 ----
public class Windowsbutton implements Button {
public void paint() {
System.out.println("You have created WindowsButton.");
}
}

public class WindowsCheckbox implements Checkbox {
public void paint() {
System.out.println("You have created WindowsCheckbox.");
}
}

// ---- MacOS 产品族 ----
public class MacOSButton implements Button {
public void paint() {
System.out.println("You have created MacOSButton.");
}
}

public class MacOSCheckbox implements Checkbox {
public void paint() {
System.out.println("You have created MacOSCheckbox.");
}
}

抽象工厂接口 GUIFactory:一个工厂同时负责创建一族产品

1
2
3
4
public interface GUIFactory {
Button createButton();
Checkbox createCheckbox();
}

具体工厂:每个工厂只生产同一风格的产品

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class WindowsFactory implements GUIFactory {
public Button createButton() {
return new Windowsbutton();
}
public Checkbox createCheckbox() {
return new WindowsCheckbox();
}
}

public class MacOSFactory implements GUIFactory {
public Button createButton() {
return new MacOSButton();
}
public Checkbox createCheckbox() {
return new MacOSCheckbox();
}
}

Application 类:只依赖抽象,通过构造器注入工厂

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Application {
private Button button;
private Checkbox checkbox;

public Application(GUIFactory factory) {
button = factory.createButton();
checkbox = factory.createCheckbox();
}

public void paint() {
button.paint();
checkbox.paint();
}
}

客户端:运行时选择工厂,注入 Application

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class ClientTest {
private static Application configureApplication() {
GUIFactory factory;
String osName = System.getProperty("os.name").toLowerCase();
if (osName.contains("mac")) {
factory = new MacOSFactory();
} else {
factory = new WindowsFactory();
}
return new Application(factory);
}

public static void main(String[] args) {
Application app = configureApplication();
app.paint();
}
}

4.5.2 面条代码(if-else 硬编码)

如果不用抽象工厂,直接暴力写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class Application {

public void paint() {
String osName = System.getProperty("os.name").toLowerCase();

if (osName.contains("mac")) {
MacOSButton button = new MacOSButton();
MacOSCheckbox checkbox = new MacOSCheckbox();
button.paint();
checkbox.paint();
} else {
Windowsbutton button = new Windowsbutton();
WindowsCheckbox checkbox = new WindowsCheckbox();
button.paint();
checkbox.paint();
}
}

// 如果还有其他业务方法要用到这些组件...
public void showDialog() {
String osName = System.getProperty("os.name").toLowerCase();
// 再来一遍一模一样的 if-else...
if (osName.contains("mac")) {
MacOSButton btn = new MacOSButton();
MacOSCheckbox cb = new MacOSCheckbox();
// ...
} else {
Windowsbutton btn = new Windowsbutton();
WindowsCheckbox cb = new WindowsCheckbox();
// ...
}
}
}

问题

  1. Application 直接依赖所有具体产品类
  2. 每个业务方法都要重复 if-else 判断
  3. 新增 Linux 平台?每个方法都要改
  4. 无法保证族内一致性——手抖写成 MacOSButton + WindowsCheckbox 编译照样通过

总结

维度 面条代码 抽象工厂模式
新增平台 修改 Application 所有方法的 if-else 新增一个工厂类 + 一组产品类,零修改已有代码
新增产品类型 在每个 if 分支里加代码 GUIFactory 接口加方法,各工厂实现
族内一致性 ❌ 无保证,全靠程序员自觉 ✅ 接口层面保证——同一工厂只产同族产品
依赖关系 Application 直接依赖全部具体类 Application 只依赖 3 个接口
开闭原则 ❌ 违反 ✅ 对扩展开放,对修改关闭
代码重复 每个方法重复 if-else 创建逻辑集中在工厂中,业务代码无重复

一句话总结:抽象工厂模式将一族相关产品的创建封装在一个工厂接口背后,客户端通过切换工厂实现来一次性切换整套产品风格,既保证了族内一致性,又实现了创建与使用的彻底解耦。