Java 23 种设计模式:从踩坑到精通 | 工厂模式 —— 还在写一坨 if-else?简单工厂→工厂方法→抽象工厂全演进
摘要:还在用一坨 if-else 创建对象?本文带你走完 简单工厂→工厂方法→抽象工厂 的完整演进路线,附 JDK、Spring、MyBatis 源码案例,彻底搞懂三种工厂的区别与选型。
📖 《Java 23 种设计模式:从踩坑到精通》
开篇:系列介绍与目录 | 上一篇:单例模式 | 当前:工厂模式 | 下一篇:抽象工厂模式
🔗 返回系列总目录
1. 从一段“if-else”屎山说起
看看下面这段代码,你是不是也曾在项目里这样写过:
if (type.equals("sms")) {
sender = new SmsSender();
} else if (type.equals("email")) {
sender = new EmailSender();
} else if (type.equals("wechat")) {
sender = new WechatSender();
}
这段代码看似简单,却藏着三个致命问题:
- 紧耦合:客户端代码直接依赖
SmsSender、EmailSender等具体类,一旦类名或构造函数变化,所有调用方都要改。 - 违反 OCP:每加一种渠道就要修改
if-else,而修改现有代码可能引入新的 Bug。 - 单元测试困难:无法注入 Mock 对象,测试时必须真实创建所有依赖。
工厂模式的核心使命就是:把对象的创建和使用分离,让变化集中在 Factory,调用方只依赖抽象接口。
2. 模式家族的三种形态
工厂模式不是单一模式,而是一个“三兄弟”家族:

| 对比维度 | 简单工厂 | 工厂方法 | 抽象工厂 |
|---|---|---|---|
| 类图复杂度 | ★ | ★★ | ★★★ |
| 核心思想 | 一个工厂根据参数创建不同产品 | 让子类决定实例化哪个产品 | 创建一系列相关产品族 |
| 产品数量 | 多种产品共用一个工厂 | 每个产品对应一个工厂 | 一个工厂生产一族产品 |
| 开闭原则 | 违反 | 符合 | 符合(产品族维度) |
2.1 一分钟选型指南
| 你的场景 | 推荐模式 | 一句话理由 |
|---|---|---|
| 产品种类少(≤3种)且基本不变 | 简单工厂 | 够用就好,过度设计无益 |
| 产品种类多、频繁新增 | 工厂方法 | 新增产品只需新加工厂类,符合开闭原则 |
| 产品成族出现、需整体切换 | 抽象工厂 | 如支付渠道、跨平台UI,保证产品族一致 |
📌 提示:简单工厂并不在 GoF 23 种设计模式之列,但它是最常用的编程习惯,也是理解工厂方法的基础。
3. 简单工厂模式 —— 把 if-else 搬个家
3.1 UML 类图

三个角色:
- 抽象产品(Product) :定义产品的公共接口;
- 具体产品(ConcreteProduct) :实现抽象产品接口;
- 工厂类(Factory) :根据参数判断,返回对应的具体产品实例。
3.2 代码实现:消息发送器
① 抽象产品
public interface Sender {
void send(String message);
}
② 具体产品
public class SmsSender implements Sender {
@Override
public void send(String message) {
System.out.println("【短信】发送:" + message);
}
}
public class EmailSender implements Sender {
@Override
public void send(String message) {
System.out.println("【邮件】发送:" + message);
}
}
③ 简单工厂
public class SenderFactory {
public static Sender create(String type) {
switch (type) {
case "sms": return new SmsSender();
case "email": return new EmailSender();
default: throw new IllegalArgumentException("未知渠道类型:" + type);
}
}
}
⚠️ 简单工厂只是把
if-else搬了家,新增产品仍需修改工厂类,违反开闭原则。
④ 客户端调用
Sender sender = SenderFactory.create("sms");
sender.send("你好,工厂模式!");
3.3 优缺点
| 优点 | 缺点 |
|---|---|
| 客户端与具体产品解耦,职责清晰 | 违反开闭原则:新增产品必须修改 switch 逻辑 |
| 创建逻辑集中管理,调用简单 | 工厂类职责过重,所有产品的创建压力集中在一个类中 |
| 适合产品种类少、变化不频繁的场景 | 使用静态方法,工厂类无法形成基于继承的等级结构 |
⚠️ 核心矛盾:简单工厂只是把
if-else从业务代码搬到了工厂类,本质上没有消除switch。当产品种类持续增长时,工厂类会变成“超级工厂”。
4. 工厂方法模式 —— 把变化隔离到子类
4.1 核心思想
简单工厂用一个类扛所有创建逻辑,工厂方法模式则把“创建”这个动作抽象成接口,让每个具体产品都有一个对应的具体工厂来创建。
一句话理解:简单工厂是“我去找工厂要产品”,工厂方法是“我去找具体的工厂子类要它擅长的产品”。
4.2 UML 类图

四个角色:
- 抽象工厂(Factory) :声明创建产品的工厂方法,返回抽象产品类型;
- 具体工厂(ConcreteFactory) :实现工厂方法,返回一个具体产品实例;
- 抽象产品(Product) :定义产品的公共接口;
- 具体产品(ConcreteProduct) :实现抽象产品接口。
4.3 代码实现
① 抽象工厂接口
public interface SenderFactory {
Sender createSender();
}
② 具体工厂类
public class SmsSenderFactory implements SenderFactory {
@Override
public Sender createSender() {
return new SmsSender();
}
}
public class EmailSenderFactory implements SenderFactory {
@Override
public Sender createSender() {
return new EmailSender();
}
}
✅ 新增产品只需新加工厂子类,完全符合开闭原则;但每种产品都要对应一个工厂类,类数量会膨胀。
③ 客户端调用
SenderFactory factory = new SmsSenderFactory();
Sender sender = factory.createSender();
sender.send("你好,工厂方法!");
4.4 优缺点
| 优点 | 缺点 |
|---|---|
| 完全符合开闭原则:新增产品只需新增一个工厂子类,零修改现有代码 | 类的数量会急剧膨胀,每个产品都要配一个工厂类 |
| 每个工厂职责单一,逻辑清晰 | 系统复杂度增加,适合产品种类较多的场景 |
| 支持基于继承的工厂等级结构 | 客户端需要知道具体工厂类的名字 |
5. 抽象工厂模式 —— 产品族一键创建
5.1 核心思想
工厂方法解决的是单一产品的创建问题,但现实开发中,我们常常需要同时创建一系列相互关联的产品(称为“产品族”)。例如一个跨平台 UI 组件库需要同时创建按钮、文本框,且必须保证它们风格统一(全 Win 风格或全 Mac 风格)。
抽象工厂模式正是为此而生:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
5.2 两个关键概念
- 产品等级结构:即产品的继承结构,例如按钮是一个抽象类,Windows 按钮和 Mac 按钮是其具体子类。
- 产品族:由同一个工厂生产的、位于不同产品等级结构中的一组产品。例如 Windows 工厂生产的 Windows 按钮 + Windows 文本框,构成一个产品族。
5.3 UML 类图

四个角色:
- 抽象工厂(AbstractFactory) :声明一组创建产品族的方法;
- 具体工厂(ConcreteFactory) :实现抽象工厂,生成一个产品族的所有产品;
- 抽象产品(AbstractProduct) :为每种产品声明接口;
- 具体产品(ConcreteProduct) :定义具体工厂生产的具体产品对象。
5.4 代码实现:跨平台 UI 组件
① 抽象产品
public interface Button {
void paint();
}
public interface TextField {
void render();
}
② 具体产品(Windows 风格)
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 风格文本框");
}
}
③ 具体产品(Mac 风格)
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 风格文本框");
}
}
④ 抽象工厂
public interface GUIFactory {
Button createButton();
TextField createTextField();
}
⑤ 具体工厂
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();
}
}
✅ 保证产品族一致性,切换工厂即可整体换肤;⚠️ 新增产品类型(如新增
Checkbox)需要修改所有工厂类。
⑥ 客户端调用
GUIFactory factory = new WinFactory(); // 只需切换这一行
Button button = factory.createButton();
TextField textField = factory.createTextField();
button.paint(); // Windows 风格按钮
textField.render(); // Windows 风格文本框
5.5 优缺点
| 优点 | 缺点 |
|---|---|
| 保证产品族兼容性:所有产品来自同一工厂,风格/协议一致 | 新增产品等级困难:如果要新增一个 Checkbox 产品,抽象工厂和所有具体工厂都要修改 |
| 客户端代码与具体平台/产品族解耦 | 类的数量进一步膨胀,系统更加复杂 |
| 切换产品族只需替换工厂实例 | 仅适用于存在产品族且产品等级相对稳定的场景 |
6. 工厂模式进化三步走
回顾三种模式的演进,你会发现它们解决的问题层层递进:

选型指南:
| 场景 | 推荐模式 | 判断依据 |
|---|---|---|
| 产品种类少(≤3 种)且基本不变 | 简单工厂 | 代码最简单,过度设计无益 |
| 产品种类多、频繁新增 | 工厂方法 | 新增产品只需新增工厂类,符合 OCP |
| 产品成族出现、需整体切换 | 抽象工厂 | 如 UI 主题、数据库驱动、支付渠道家族 |
💡 黄金法则:能用简单工厂就别上工厂方法,能用工厂方法就别上抽象工厂。模式是为解决问题服务的,不是为了炫技。
📌 正在追更《Java 23 种设计模式:从踩坑到精通》?
点击右上角「关注」,不错过下一篇——抽象工厂:产品族创建,如何避免“一锅粥”?
7. 框架中的工厂模式
7.1 JDK:Calendar.getInstance() —— 简单工厂
JDK 中的 java.util.Calendar 类是简单工厂模式的经典应用。
// JDK 源码简化示意
public abstract class Calendar {
public static Calendar getInstance() {
return createCalendar(TimeZone.getDefault(), Locale.getDefault());
}
private static Calendar createCalendar(TimeZone zone, Locale locale) {
// 根据地区和语言,返回不同的 Calendar 子类实例
if ("th".equals(locale.getLanguage()) && "TH".equals(locale.getCountry())) {
return new BuddhistCalendar(zone, locale);
}
return new GregorianCalendar(zone, locale);
}
}
调用方只需 Calendar.getInstance(),无需知道背后到底是 GregorianCalendar 还是 BuddhistCalendar。
7.2 Spring IoC 容器 —— 工厂方法 + 抽象工厂
Spring 的 BeanFactory 是工厂方法模式的经典实现。
// BeanFactory 是抽象工厂,声明了获取 Bean 的接口
public interface BeanFactory {
Object getBean(String name);
<T> T getBean(Class<T> requiredType);
// ...
}
而 FactoryBean<T> 则是工厂方法的进一步封装——它本身是一个 Bean,但其 getObject() 方法负责创建并返回另一个复杂对象。
BeanFactory是管理所有 Bean 的容器本身(抽象工厂),而FactoryBean是容器中用于生产特定复杂对象的特殊 Bean(工厂方法)。二者一纵一横,共同构成了 Spring 强大的对象创建体系。
7.3 MyBatis:SqlSessionFactory —— 抽象工厂
MyBatis 的 SqlSessionFactory 负责创建 SqlSession 实例,涉及数据库连接、事务管理等复杂配置,是典型的抽象工厂模式:
public interface SqlSessionFactory {
SqlSession openSession();
// ...
}
8. 常见误区与面试高频题
❌ 误区1:简单工厂就是工厂方法
简单工厂不属于 GoF 23 种设计模式,它使用静态方法且只有一个工厂类;工厂方法则通过抽象工厂接口 + 多个具体工厂子类实现多态创建。
❌ 误区2:工厂方法可以替代简单工厂
如果产品种类很少且变化不大,简单工厂足够用了。强行上工厂方法只会导致类数量膨胀,得不偿失。
❌ 误区3:抽象工厂比工厂方法“高级”
它们是解决不同问题的工具,不存在高低之分。如果产品之间不构成“产品族”,抽象工厂反而是过度设计。
💡 面试高频追问
- 简单工厂违背了什么原则? → 开闭原则(OCP),新增产品需修改工厂类中的
switch逻辑。 - 工厂方法和抽象工厂的本质区别是什么? → 工厂方法解决单一产品的横向扩展,抽象工厂解决产品族的纵向创建。
BeanFactory和FactoryBean分别是什么模式? →BeanFactory是抽象工厂模式,FactoryBean是工厂方法模式。
9. 六大设计原则在工厂模式中的体现
| 设计原则 | 在工厂模式中的体现 |
|---|---|
| 单一职责原则(SRP) | 每个具体工厂只负责创建一种产品,职责内聚 |
| 开闭原则(OCP) | 工厂方法新增产品只需加新工厂类,无需修改现有代码 |
| 里氏替换原则(LSP) | 客户端使用抽象产品类型,具体产品可任意替换 |
| 依赖倒置原则(DIP) | 客户端依赖抽象工厂和抽象产品接口,而非具体实现类 |
| 接口隔离原则(ISP) | 抽象工厂按产品族维度拆分接口,避免“胖接口” |
| 迪米特法则(LoD) | 客户端只需知道工厂和产品接口,无需了解内部创建细节 |
10. 总结对比一览
| 对比维度 | 简单工厂 | 工厂方法 | 抽象工厂 |
|---|---|---|---|
| GoF 收录 | 否 | 是 | 是 |
| 工厂数量 | 1 个 | N 个(每产品一工厂) | N 个(每产品族一工厂) |
| 产品数量 | 多种 | 一种(每工厂) | 一族(多产品) |
| 开闭原则 | 违反 | 符合 | 部分符合 |
| 类复杂度 | ★☆☆☆ | ★★★☆ | ★★★★ |
| 推荐指数 | ★★★☆ | ★★★★★ | ★★★★ |
| 典型应用 | Calendar.getInstance() | Spring FactoryBean | MyBatis SqlSessionFactory |
✅ 建议:日常开发中,优先考虑简单工厂(够用即止);产品种类多且频繁扩展时升级为工厂方法;系统存在产品族概念时(如多端 UI、多数据库)果断使用抽象工厂。
附录:| Factory UML 源码
Simple Factory UML 源码
@startuml
skinparam backgroundColor #FEFEFE
interface Product {
+ operation()
}
class ConcreteProductA implements Product
class ConcreteProductB implements Product
class SimpleFactory {
+ createProduct(type) : Product
}
SimpleFactory ..> Product : creates
@enduml
SenderFactory UML 源码
@startuml
skinparam backgroundColor #FEFEFE
skinparam classBorderColor #333333
skinparam classFontColor #333333
skinparam arrowColor #555555
skinparam classBackgroundColor #F9F9F9
interface SenderFactory {
+ createSender(): Sender
}
class SmsSenderFactory implements SenderFactory {
+ createSender(): Sender
}
class EmailSenderFactory implements SenderFactory {
+ createSender(): Sender
}
interface Sender {
+ send(message: String): void
}
class SmsSender implements Sender {
+ send(message: String): void
}
class EmailSender implements Sender {
+ send(message: String): void
}
SmsSenderFactory ..> SmsSender : creates
EmailSenderFactory ..> EmailSender : creates
@enduml
GUIFactory UML 源码
@startuml
skinparam backgroundColor #FEFEFE
skinparam classBorderColor #333333
skinparam classFontColor #333333
skinparam arrowColor #555555
skinparam classBackgroundColor #F9F9F9
interface GUIFactory {
+ createButton(): Button
+ createTextField(): TextField
}
class WinFactory implements GUIFactory {
+ createButton(): Button
+ createTextField(): TextField
}
class MacFactory implements GUIFactory {
+ createButton(): Button
+ createTextField(): TextField
}
interface Button {
+ paint(): void
}
interface TextField {
+ render(): void
}
class WinButton implements Button {
+ paint(): void
}
class MacButton implements Button {
+ paint(): void
}
class WinTextField implements TextField {
+ render(): void
}
class MacTextField implements TextField {
+ render(): void
}
WinFactory ..> WinButton : creates
WinFactory ..> WinTextField : creates
MacFactory ..> MacButton : creates
MacFactory ..> MacTextField : creates
@enduml
🧭 《Java 23 种设计模式:从踩坑到精通》快速导航
- 开篇:系列介绍与目录
- 上一篇:单例模式
- 当前:工厂模式(你在这里)
- 下一篇:抽象工厂模式
- 创建型模式汇总:单例、工厂、建造者、原型
- 结构型模式汇总:适配器、装饰器、代理……
- 行为型模式汇总:观察者、策略、模板方法……
🔔 关注《Java 23 种设计模式:从踩坑到精通》,用 25 篇文章彻底吃透设计模式,让代码设计成为你的本能。
📦 福利预告:全系列代码及 UML 源码将在完结时统一打包开放,点击「关注」「收藏」第一时间获取。
🚀 下一篇:抽象工厂模式——支付渠道 + 收款渠道如何保证成套创建?跨平台 UI 如何一键换肤?
📌 除了设计模式,我也在深挖智能物流实战(出版社WMS、托盘调度、机器学习落地)。如果你对技术在不同领域的实战感兴趣,欢迎点击我的头像,看看专栏《出版社物流WMS智能调度实战》。技术相通,思路可鉴。
1232




被折叠的 条评论
为什么被折叠?



