软考设计模式系列第 2 篇。上一篇我们聊了简单工厂模式 ------用一个工厂类根据参数"按需分配"具体产品。但简单工厂有个局限:它只能生产单一产品等级 (比如只生产按钮,或只生产文本框)。如果系统需要同时生产一组配套的产品 (比如 Windows 风格的按钮 + Windows 风格的文本框,或 Mac 风格的按钮 + Mac 风格的文本框),简单工厂就力不从心了。这时,抽象工厂模式登场。
一、模式定义
抽象工厂模式(Abstract Factory Pattern) 是一种创建型设计模式,它为创建一组相关或相互依赖的对象提供一个接口,而且无需指定它们的具体类。
通俗地说:抽象工厂不生产"单个产品",它生产"整套产品族"。
1.1 核心概念:产品族 vs 产品等级
| 概念 | 含义 | 举例 |
|---|---|---|
| 产品等级结构 | 同一类产品的继承结构 | 按钮 ← Windows按钮 / Mac按钮 |
| 产品族 | 同一具体工厂生产的所有产品 | Windows工厂:Windows按钮 + Windows文本框 + Windows组合框 |
抽象工厂解决的核心问题是:保证同一个产品族中的对象被一起使用,避免 Windows 按钮配 Mac 文本框这种"混搭"问题。
二、UML 类图与角色
┌─────────────────────┐
│ <<interface>> │
│ AbstractFactory │
│ + createButton() │
│ + createTextField() │
└──────────┬──────────┘
│
┌──────┴──────┐
▼ ▼
┌───────────┐ ┌───────────┐
│ WinFactory│ │ MacFactory│
│(具体工厂1) │ │(具体工厂2 │
└────┬──────┘ └────┬──────┘
│ │
▼ ▼
┌─────────────┐ ┌─────────────┐
│<<interface>>│ │<<interface>>│
│ Button │ │ TextField │
│ + draw() │ │ + display() │
└────┬────────┘ └─────┬───────┘
│ │
┌─┴──┐ ┌─┴──┐
▼ ▼ ▼ ▼
┌──────┐┌──────┐ ┌─────┐┌─────┐
│WinBtn││MacBtn│ │WinTF││MacTF│
└──────┘└──────┘ └─────┘└─────┘
角色说明
| 角色 | 职责 |
|---|---|
| AbstractFactory | 抽象工厂接口,声明创建各个产品的方法 |
| ConcreteFactory | 具体工厂(如 WinFactory、MacFactory),实现抽象工厂接口,生产同一产品族的所有对象 |
| AbstractProduct | 抽象产品接口(如 Button、TextField) |
| ConcreteProduct | 具体产品(如 WinButton、MacButton) |
| Client | 客户端,通过抽象工厂和抽象产品接口操作,不依赖具体类 |
三、Java 代码实现
以"跨平台 UI 组件库"为例,实现 Windows 和 Mac 两个产品族。
3.1 抽象产品接口
java
// 抽象产品:按钮
public interface Button {
void draw();
}
// 抽象产品:文本框
public interface TextField {
void display();
}
3.2 具体产品(Windows 族)
java
public class WinButton implements Button {
@Override
public void draw() {
System.out.println("绘制 Windows 风格按钮");
}
}
public class WinTextField implements TextField {
@Override
public void display() {
System.out.println("显示 Windows 风格文本框");
}
}
3.3 具体产品(Mac 族)
java
public class MacButton implements Button {
@Override
public void draw() {
System.out.println("绘制 Mac 风格按钮");
}
}
public class MacTextField implements TextField {
@Override
public void display() {
System.out.println("显示 Mac 风格文本框");
}
}
3.4 抽象工厂
java
public interface GUIFactory {
Button createButton();
TextField createTextField();
}
3.5 具体工厂
java
public class WinFactory implements GUIFactory {
@Override
public Button createButton() {
return new WinButton();
}
@Override
public TextField createTextField() {
return new WinTextField();
}
}
public class MacFactory implements GUIFactory {
@Override
public Button createButton() {
return new MacButton();
}
@Override
public TextField createTextField() {
return new MacTextField();
}
}
3.6 客户端代码
java
public class Client {
public static void main(String[] args) {
// 通过配置或参数决定使用哪个产品族
String osType = "Windows"; // 或 "Mac"
GUIFactory factory;
if (osType.equals("Windows")) {
factory = new WinFactory();
} else {
factory = new MacFactory();
}
// 客户端只依赖抽象接口
Button btn = factory.createButton();
TextField tf = factory.createTextField();
btn.draw();
tf.display();
// 输出:绘制 Windows 风格按钮
// 显示 Windows 风格文本框
}
}
四、软考代码填空常见考法
软考下午题中,抽象工厂模式通常以代码填空形式出现,重点考察以下位置:
典型填空点 1:抽象工厂接口声明
java
public interface GUIFactory {
Button (1); // 创建按钮
TextField (2); // 创建文本框
}
答案: (1) createButton() (2) createTextField()
典型填空点 2:具体工厂实现
java
public class WinFactory implements GUIFactory {
public Button createButton() {
return (3); // 填空
}
}
答案: (3) new WinButton()
典型填空点 3:客户端依赖关系
java
public class Client {
private (4) factory; // 声明类型
private Button btn;
public Client((5) factory) { // 参数类型
this.factory = factory;
}
}
答案: (4) GUIFactory (5) GUIFactory
软考核心原则 :客户端应依赖抽象接口(GUIFactory / Button),而非具体类(WinFactory / WinButton)。
五、抽象工厂 vs 工厂方法 vs 简单工厂
表格
| 维度 | 简单工厂 | 工厂方法 | 抽象工厂 |
|---|---|---|---|
| 产品数量 | 单一产品等级 | 单一产品等级 | 多个产品等级(产品族) |
| 工厂数量 | 一个工厂类 | 一个产品对应一个工厂 | 一个产品族对应一个工厂 |
| 扩展性 | 新增产品需改工厂 | 新增产品等级只需加工厂 | 新增产品族只需加工厂;新增产品等级需改所有工厂 |
| 复杂度 | 低 | 中 | 高 |
| 软考考频 | 中 | 高 | 高 |
关键区别记忆口诀
工厂方法 :一个工厂造"一种东西"的不同款式;
抽象工厂:一个工厂造"一套东西"的配套款式。
六、优缺点分析
优点
-
产品族一致性:同一工厂生产的对象一定是兼容的,不会出现风格混搭。
-
隔离具体类:客户端只依赖抽象接口,符合依赖倒置原则。
-
易于切换产品族 :将
WinFactory换成MacFactory,整套 UI 风格统一切换。
缺点
-
扩展产品等级困难 :如果要在产品族中新增"下拉框(ComboBox)",需要修改所有具体工厂类(WinFactory、MacFactory...),违反开闭原则。
-
类数量膨胀:N 个产品族 × M 个产品等级 = 大量类文件。
七、适用场景
-
系统需要独立于产品的创建、组合和表示(如跨平台 UI 框架)。
-
系统需要由多个产品族中的一个来配置(如不同数据库厂商:MySQL 族 / Oracle 族)。
-
需要强调一系列相关产品的配套使用(如游戏开发中的"精灵族":精灵建筑 + 精灵兵种 + 精灵魔法)。
八、软考备考要点总结
表格
| 考点 | 要点 |
|---|---|
| 定义填空 | "为创建一组相关或相互依赖的对象提供接口,无需指定具体类" |
| 角色识别 | 能区分 AbstractFactory / ConcreteFactory / AbstractProduct / ConcreteProduct |
| 代码填空 | 工厂返回值类型为具体产品 ;客户端声明类型为抽象产品/抽象工厂 |
| 与工厂方法区别 | 抽象工厂处理产品族 (多个产品等级);工厂方法处理单一产品等级 |
| 扩展性陷阱 | 新增产品族容易(加工厂);新增产品等级难(改所有工厂)------这是软考常考的缺点 |
九、完整记忆图
java
抽象工厂 GUIFactory
│
┌───────┴───────┐
▼ ▼
WinFactory MacFactory ← 产品族(容易扩展:新增 LinuxFactory)
│ │
├─Button ├─Button
│ │ │ │
│ WinButton │ MacButton
│ │
├─TextField ├─TextField
│ │ │ │
│ WinTF MacTF
│
└─ComboBox? ← 新增产品等级(困难:所有工厂都要改)
十、结语
抽象工厂模式是软考下午题中出镜率极高 的创建型模式。掌握它的关键在于理解**"产品族"** 这个概念------它不是生产一个对象,而是生产一套配套对象 。记住它的优缺点(扩展产品族易、扩展产品等级难),分清它与工厂方法模式的边界,代码填空时留意**"抽象类型声明,具体类型实例化"**这一原则,软考中遇到抽象工厂的题目就能从容应对。