Java 23 种设计模式:从踩坑到精通 | 抽象工厂 —— 支付/收款如何成套创建?跨平台 UI 如何一键换肤?

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 是一个抽象接口,WinButtonMacButton 是它的具体实现。ButtonTextField 属于不同的产品等级结构。
  • 产品族 :由同一个工厂生产的、位于不同产品等级结构中的一组产品。例如 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智能调度实战》。技术相通,思路可鉴。

相关推荐
葱卤山猪1 小时前
C++17 联合体
开发语言·c++
方也_arkling1 小时前
【Java-Day11】抽象类和抽象方法
java·开发语言
XS0301061 小时前
并发编程 七
java
Ulyanov1 小时前
深入QML-Python通信 构建响应式交互界面的桥梁设计:QML+PySide6现代开发入门(五)
开发语言·python·算法·交互·qml·系统仿真
就叫_这个吧1 小时前
JavaScript中常用事件示例展示附源码
开发语言·javascript·html
不会C语言的男孩1 小时前
C++ Primer Plus 第9章:内存模型和名称空间
开发语言·c++
zz34572981131 小时前
函数:python与c语言
c语言·开发语言·python
峥嵘life2 小时前
Android getprop 属性限制详解:User 版本属性获取问题分析
android·开发语言·python·学习
郝学胜-神的一滴2 小时前
Qt 高级开发 019:从零定制登录窗口按钮、Logo 样式与交互悬浮效果
开发语言·c++·qt·程序人生·交互·用户界面