Java 23 种设计模式:从踩坑到精通 | 抽象工厂 ------ 支付/收款如何成套创建?跨平台 UI 如何一键换肤?
摘要 :当系统需要同时创建多个有内在关联 的产品对象时(如不同支付渠道的支付+收款,或跨平台 UI 的按钮+文本框),简单的工厂方法会导致工厂数量爆炸且难以保证产品族一致性。本文带你深入抽象工厂模式,用支付产品族 和跨平台 UI 换肤两个实战案例,彻底搞懂"产品族"与"产品等级结构",并给出与工厂方法的终极选型指南。
📖 《Java 23 种设计模式:从踩坑到精通》开篇:系列介绍与目录 | 上一篇:工厂模式 | 当前:抽象工厂模式 | 下一篇:建造者模式
🔗 返回系列总目录
1. 从一个"换肤"需求说起
假设你正在开发一套跨平台的 UI 组件库,需要同时支持 Windows 风格和 Mac 风格。每个风格的组件都是一整套的:按钮、文本框、下拉框...... 如果直接用 new 创建:
java
Button btn = new WinButton();
TextField tf = new WinTextField();
当需要切换到 Mac 风格时,你必须把每一个 new 的地方都改成 new MacButton()、new MacTextField()。这不仅繁琐,还容易漏改,导致界面风格"串味"------变成一锅粥。
更麻烦的是,如果将来要新增一个 Linux 风格,所有创建代码都得再改一遍。
抽象工厂模式正是为了解决这类"产品族"创建问题而生的:它把一族相关的产品交给一个专门的工厂来统一生产,切换工厂就等于切换整个产品族,保证风格绝对一致。
2. 模式定义与核心概念
抽象工厂模式 提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。它属于 创建型设计模式。
2.1 两个关键概念
- 产品等级结构 :即产品的继承结构。例如
Button是一个抽象接口,WinButton和MacButton是它的具体实现。Button和TextField属于不同的产品等级结构。 - 产品族 :由同一个工厂生产的、位于不同产品等级结构中的一组产品。例如
WinFactory生产的WinButton+WinTextField构成一个 Windows 产品族。
2.2 一分钟选型指南
| 你的场景 | 推荐模式 | 一句话理由 |
|---|---|---|
| 单一产品类型,但需要灵活扩展 | 工厂方法 | 每新增一个产品只需加一个工厂类,符合开闭原则 |
| 多个产品类型成套出现,需要整体切换 | 抽象工厂 | 一个工厂生产一族产品,保证风格/协议一致 |
| 产品类型数量不固定,未来可能新增产品类型 | 谨慎使用抽象工厂 | 新增产品类型(如新增 Checkbox)需要改动所有工厂,代价高 |
3. UML 类图

4. 代码实现:跨平台 UI 组件库(经典案例)
4.1 抽象产品
java
public interface Button {
void paint();
}
public interface TextField {
void render();
}
4.2 具体产品(Windows 风格)
java
public class WinButton implements Button {
@Override
public void paint() {
System.out.println("渲染 Windows 风格按钮");
}
}
public class WinTextField implements TextField {
@Override
public void render() {
System.out.println("渲染 Windows 风格文本框");
}
}
4.3 具体产品(Mac 风格)
java
public class MacButton implements Button {
@Override
public void paint() {
System.out.println("渲染 Mac 风格按钮");
}
}
public class MacTextField implements TextField {
@Override
public void render() {
System.out.println("渲染 Mac 风格文本框");
}
}
4.4 抽象工厂
java
public interface GUIFactory {
Button createButton();
TextField createTextField();
}
4.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(); }
}
✅ 切换工厂只需改动一行代码,整个产品族同步切换,彻底杜绝"串味"风险。
4.6 客户端
java
GUIFactory factory = new WinFactory();
Button btn = factory.createButton();
TextField tf = factory.createTextField();
btn.paint(); // Windows 风格按钮
tf.render(); // Windows 风格文本框
// 一键换肤
factory = new MacFactory();
btn = factory.createButton();
tf = factory.createTextField();
5. 代码实现:支付产品族(支付 + 收款)
5.1 抽象产品
java
public interface IPay { void pay(); }
public interface ICollect { void collect(); }
5.2 具体产品(阿里系)
java
public class AliPay implements IPay {
public void pay() { System.out.println("【支付宝】支付成功"); }
}
public class AliCollect implements ICollect {
public void collect() { System.out.println("【支付宝】收款到账"); }
}
5.3 具体产品(微信系)
java
public class WxPay implements IPay {
public void pay() { System.out.println("【微信】支付成功"); }
}
public class WxCollect implements ICollect {
public void collect() { System.out.println("【微信】收款到账"); }
}
5.4 抽象工厂
java
public abstract class PayFactory {
public void init() { System.out.println("初始化支付环境..."); }
public abstract IPay createPay();
public abstract ICollect createCollect();
}
5.5 具体工厂
java
public class AliFactory extends PayFactory {
public IPay createPay() { return new AliPay(); }
public ICollect createCollect() { return new AliCollect(); }
}
public class WxFactory extends PayFactory {
public IPay createPay() { return new WxPay(); }
public ICollect createCollect() { return new WxCollect(); }
}
✅ 同一个工厂保证了支付和收款使用同一渠道,客户端只与抽象工厂交互,扩展新渠道只需新增一个工厂类。
5.6 客户端
java
PayFactory factory = new AliFactory();
factory.init();
factory.createPay().pay();
factory.createCollect().collect();
factory = new WxFactory();
factory.init();
factory.createPay().pay();
6. 优缺点一览
| 优点 | 缺点 |
|---|---|
| 保证产品族一致性:同一工厂的所有产品风格/协议天然兼容 | 新增产品等级困难 :若要新增一个产品类型(如 Checkbox),抽象工厂及所有具体工厂都要修改 |
| 切换产品族极其简单:只需更换工厂实例 | 类数量增加,系统复杂 |
| 客户端与具体产品解耦 | 仅适用于存在产品族且产品等级相对稳定的场景 |
7. 抽象工厂 vs 工厂方法
| 对比维度 | 工厂方法 | 抽象工厂 |
|---|---|---|
| 解决问题 | 单一产品的横向扩展 | 产品族的纵向创建 |
| 工厂数量 | 每个产品一个工厂 | 每个产品族一个工厂 |
| 扩展方向 | 新增产品只需新增工厂类(符合 OCP) | 新增产品族只需新增工厂类(符合 OCP),新增产品类型需修改抽象工厂(违反 OCP) |
| 典型场景 | 单一支付方式扩展 | 支付渠道 + 收款渠道的成套创建 |
💡 口诀:一个产品横向扩展用工厂方法,一族产品成套创建用抽象工厂。
8. 框架中的抽象工厂
- MyBatis 的
SqlSessionFactory:负责创建SqlSession及关联的Configuration,确保它们属于同一个数据库环境。 - JDBC 的
DataSource:提供getConnection(),连接、语句、结果集构成一套完整的产品族。 - Spring 的
AbstractBeanFactory:保证 Bean 的依赖、作用域、生命周期管理的一致性。
9. 常见误区与面试高频题
❌ 误区1:抽象工厂就是升级版的工厂方法
两者解决不同维度的问题,没有高低之分。
❌ 误区2:抽象工厂完全符合开闭原则
只在产品族维度上符合 OCP,在产品等级维度上违反 OCP。
💡 面试高频追问
- 抽象工厂如何保证产品族一致性? → 同一工厂实例创建的所有产品都属于同一产品族。
- 什么时候该用抽象工厂而不是工厂方法? → 当系统需要创建的对象之间存在"成套"或"风格统一"的强关联时。
- 抽象工厂的缺点? → 新增产品类型困难,需要修改抽象工厂及所有具体工厂。
10. 六大设计原则在抽象工厂中的体现
| 设计原则 | 体现 |
|---|---|
| 单一职责(SRP) | 每个具体工厂只负责一个产品族 |
| 开闭原则(OCP) | 新增产品族无需修改现有代码(但新增产品类型需修改) |
| 里氏替换(LSP) | 所有产品族都可替换抽象工厂和抽象产品 |
| 依赖倒置(DIP) | 客户端依赖抽象,不依赖具体工厂 |
| 接口隔离(ISP) | 抽象工厂按产品族拆分接口,避免臃肿 |
| 迪米特法则(LoD) | 客户端只与抽象工厂交互,不接触具体产品 |
附录:| Abstract Factory UML 源码
@startuml
skinparam backgroundColor #FEFEFE
interface Button {
+ paint()
}
interface TextField {
+ render()
}
class WinButton implements Button
class MacButton implements Button
class WinTextField implements TextField
class MacTextField implements TextField
interface GUIFactory {
+ createButton() : Button
+ createTextField() : TextField
}
class WinFactory implements GUIFactory
class MacFactory implements GUIFactory
WinFactory ..> WinButton : creates
WinFactory ..> WinTextField : creates
MacFactory ..> MacButton : creates
MacFactory ..> MacTextField : creates
@enduml
🧭 《Java 23 种设计模式:从踩坑到精通》快速导航
- 开篇:系列介绍与目录
- 上一篇:工厂模式
- 当前:抽象工厂模式(你在这里)
- 下一篇:建造者模式 🚧 即将发布
- 创建型模式汇总:单例、工厂、建造者、原型
- 结构型模式汇总:适配器、装饰器、代理......
- 行为型模式汇总:观察者、策略、模板方法......
🔔 关注《Java 23 种设计模式:从踩坑到精通》,用 25 篇文章彻底吃透设计模式。
📦 福利预告 :全系列代码及 UML 源码将在完结时统一打包开放,点击「关注」「收藏」第一时间获取。
🚀 下一篇 :建造者模式 ------ 构造器参数太多?试试链式调用!🚧 即将发布,敬请关注!
📌 除了设计模式,我也在深挖智能物流实战 (WMS、托盘调度、机器学习落地)。欢迎点击头像,看看专栏 《出版社物流WMS智能调度实战》。技术相通,思路可鉴。