设计模式(二)——三种工厂的对比
这是一个非常经典的问题。
很多朋友分不清这三个,是因为它们都在干同一件事:把 new 关键字隐藏起来。但是它们的复杂度和解决的问题边界不同。
我们可以把它们看作是工厂发展的三个阶段:从“小作坊”到“大工厂”再到“产业生态链”。
先说结论:
- 简单工厂(静态工厂):一个工厂类(通常用静态方法)包揽所有同类产品的创建,无抽象工厂接口,产品可实现同一接口。(非 GOF 23 种设计模式,是基础封装)
- 基础封装,一个工厂对应多个产品,通常只有一个相关工厂
- 工厂方法:定义抽象工厂接口(仅负责创建「单一产品」),每个具体产品对应一个具体工厂,工厂方法延迟到具体工厂实现。
- 工厂方法模式本质就是对获取对象过程的抽象
- 抽象工厂:定义抽象工厂接口(负责创建「一系列相关 / 依赖的产品(产品族)」),每个具体工厂对应一个产品族,负责创建该产品族的所有产品。
- 一个工厂对应多个产品
- 有多个不同品牌的工厂,所以需要一个抽象工厂
- 抽象工厂定义多个产品的抽象生产方法
- 具体工厂定义多个产品的具体生产方法
- 由于产品有多个品牌(一个品牌对应一个工厂),所以会有抽象产品
- 对应有多个“品牌”(mysql,oracle),有多种“产品”的场景(事务管理者,事务状态等)
一、 三种工厂模式的对比与区别
简单工厂 (Simple Factory) —— 其实不是标准设计模式,而是一种编程习惯
- 别名:静态工厂方法 (Static Factory Method)。
- 核心逻辑:一个工厂类,一个静态方法,一堆
if-else。
将新建对象的逻辑放置在工厂中(里层逻辑可以内置在类中)
当然,如果出现了创建对象逻辑相当复杂的情况,可以使用工厂方法模式,创建工厂,在工厂中封装复杂逻辑(工厂方法模式本质就是对获取对象过程的抽象)
通常配合策略模式一起使用,把具体逻辑放到策略类中,客户通过简单工厂取出策略,调用被实现的抽象方法。
为了实现可扩展性,可以使用 枚举 + Map + 提供获取方法 的方式,避免大堆 if-else,见策略模式。
简单工厂和工厂方法其实是不互斥的,可以将复杂的创建逻辑封装到工厂方法中,然后将 new 对象的选择过程放在简单工厂模式中(选择调用不同工厂的 getInstance 方法创建不同的产品对象)。
如果不用 spring 的情况下,简单工厂可能会耦合,但是工厂方法不会耦合。使用工厂方法的情况下,如果有了新的产品,新增一个新的工厂类即可(新增)。如果工厂方法还整合了简单工厂,那么新增产品时还需要对简单工厂进行修改,这违反了开闭原则。
不过如果是 spring 的话,策略模式笔记中演示的那种方式已经实现解耦了。
甚至可以配合反射 + 配置文件使用,用指定类名的方式来创建对象

- 类比:自动售货机。你塞进去钱,按下按钮(参数),它吐出你要的可乐或雪碧(产品)。你不需要知道可乐是怎么灌装的。
- 代码模型:
public class PhoneFactory {
public static Phone createPhone(String type) {
if ("iphone".equals(type)) return new IPhone();
else if ("huawei".equals(type)) return new HuaweiPhone();
else if("xiaomi".equals(type)) return new XiaomiPhone();
return null;
}
}
- 缺点:违背开闭原则 (OCP)。如果要卖小米手机,你必须修改
PhoneFactory的源代码(加个else if)。
不适用场景
- 产品需要频繁新增(比如每月都要新增 1-2 种订单类型),每次新增都要修改工厂类的
if/else逻辑,违反「开闭原则」,维护成本越来越高。 - 产品创建逻辑复杂且差异极大(比如每种订单的创建逻辑都完全不同,无共性可提取),工厂类会变得臃肿,难以维护。
- 对系统的「可扩展性」「可维护性」要求极高(如大型分布式系统的核心模块)。
工厂方法 (Factory Method) —— 标准的 GoF 模式
若每个产品的创建逻辑相对独立或困难(差异较大,或难以通过简单 if/else 封装)时,此时不宜仅仅采用简单工厂模式,则需要工厂方法单独封装。
由于提供了统一的创造接口,所以工厂方法的解耦是相对轻松的。
- 核心逻辑:把“生产”推迟到子类。 定义一个创建对象的接口,让子类决定实例化哪一个类。工厂方法模式本质就是对获取对象过程的抽象
- 生活类比:专卖店体系。
- 想要苹果手机?去“苹果手机工厂店”(只生产苹果)。
- 想要华为手机?去“华为手机工厂店”(只生产华为)。
- 总公司只制定“生产标准”,不负责具体生产。
- 代码模型:
// 抽象工厂接口
// Phone 统一通过这个接口获取,日后想增加具体手机只需增加实现类,可扩展性大
public interface PhoneFactory {
Phone createPhone();
}
// 具体工厂 A
public class IPhoneFactory implements PhoneFactory {
// 一大堆复杂逻辑
public Phone createPhone() { return new IPhone(); }
}
// 具体工厂 B
public class HuaweiFactory implements PhoneFactory {
// 一大堆复杂逻辑
public Phone createPhone() { return new HuaweiPhone(); }
}
- 优点:符合****开闭原则。要增加小米手机,新建一个
XiaomiFactory即可,不用改原有代码。 - 缺点:类爆炸。每增加一种产品,就要增加一个对应的工厂类(因为工厂方法中的工厂是对类创建过程的封装)。
不适用场景
- 产品种类极少且固定(比如只有 2 种产品),用工厂方法会增加类的数量(额外的抽象工厂、具体工厂),过度设计,不如简单工厂简洁。
- 产品之间存在依赖关系(比如创建「手机」的同时,需要创建「手机充电器」「手机耳机」,三者是一套产品),工厂方法只关注单一产品,无法保证产品之间的兼容性。
- 对「类的数量」敏感(如嵌入式系统、轻量级应用),产品增多时会出现「工厂爆炸」(一个产品对应一个工厂),增加维护成本。
抽象工厂 (Abstract Factory) —— 最复杂,用于“产品族”
需要创建「一系列相关 / 依赖的产品(产品族)」、保证产品族内的兼容性、扩展产品族而非单一产品,追求「产品族的整体封装与兼容」。
产品族 vs 产品等级结构
| 概念 | 示例(电子设备产品) |
|---|---|
| 产品等级结构(单一产品接口) | 手机、充电器、耳机(每个都是独立的产品等级,有各自的抽象接口) |
| 产品族(一系列相关产品) | 华为产品族(华为手机、华为充电器、华为耳机)、苹果产品族(苹果手机、苹果充电器、苹果耳机) |
| 抽象工厂的核心 | 创建「产品族」,而不是单一产品等级 |
核心关键点:
- 产品属于「多个相关的产品等级结构」(即「产品族」),比如「手机」「充电器」「耳机」是三个产品等级,「华为产品族」包含华为手机、华为充电器、华为耳机,三者相互依赖、兼容。
- 客户端需要的是「一套产品」,而不是单一产品,且需要保证「同一产品族的产品能相互配合」。
- 优先考虑「产品族的扩展性」,接受「单一产品扩展困难」的代价。
优势:① 保证产品族兼容性:HuaWeiFactory 创建的产品必然是华为系列,可无缝交互,避免出现「华为手机搭配苹果平板」的不兼容场景;② 扩展产品族容易:新增「小米产品族」时,只需新增 XiaoMiFactory 和对应的三个产品类,无侵入性。③ 切换品牌时,只需更换具体工厂,即可获取该工厂的全套产品,无需逐个修改产品,避免产品不配套导致的问题(这里类比切换环境,产品类比配置,更好理解) ④ 有的时候还能做到同一品牌产品族之间的联动(类比不同职业的装备)
在《大话设计模式》一书中,抽象工厂最终还是被改成了简单工厂模式。在简单的逻辑中,抽象工厂不如简单工厂,直接把创建具体具体产品的类放到简单工厂中
原来的逻辑:
-
核心逻辑:生产“全家桶”。它创建的不是一个对象,而是一组相关的对象(产品族)。
-
核心部分:
- 工厂接口(由具体品牌实现,定义创造抽象产品的方法: 定义创建「事务管理器」、「事务状态」、「事务连接」的方法)
- 具体工厂 (返回具体产品,创建 JDBC 产品族、创建 Hibernate 产品族)
- 工厂的意义是创造
- 产品接口(品牌产品实现,定义产品的功能:事务管理器可以干嘛,事务连接可以干嘛,事务状态可以干嘛,由于数据库的事务操作都是通用的,而具体逻辑不同,所以要有产品接口这一层产品的抽象)
- 具体产品(被返回的对象,在方法里面实现各个产品的具体逻辑)
-
生活类比:数码套装。
- 苹果工厂:生产 {iPhone, iPad, Mac} —— 这是一整套,风格统一,接口兼容。
- 华为工厂:生产 {Mate, MatePad, MateBook} —— 也是一整套。
- 你不能让苹果工厂生产 MateBook。
-
代码模型:
// 工厂接口,定义创造抽象产品的方法
public interface ElectronicsFactory {
Phone createPhone();
Computer createComputer();
}
// 苹果全家桶工厂
public class AppleFactory implements ElectronicsFactory {
// 对的,Phone 也是个接口
// 如果这里是华为工厂,那么 createPhone 生产出来的就是华为手机
public Phone createPhone() { return new IPhone(); }
public Computer createComputer() { return new Mac(); }
}
// 使用,指定要那个工厂生产的东西,除了 new 的工厂不一样之外,其他都是通用
// 后续要增加新的电子产品时:工厂接口和工厂需要加上对应方法
// 抽象产品增加一个,具体产品增加品牌数个
ElectronicsFactory fac1 = new AppleFacrory();
Phone applePhone = fac1.createPhone();
applePhone.call(); // 打电话:信号不好
ElectronicsFactory fac2 = new XiaomiFactory();
Phonw xiaomiPhone = fac2.createPhone();
xiaomiPhone,call(); // 打电话:接通率了
- 应用场景:需要保证客户端只使用同一个产品族的对象。比如:换肤功能(这套皮肤的 按钮、滚动条、窗口背景 必须风格统一);跨数据库操作(Connection, Statement, ResultSet 必须都是 MySQL 的,或者都是 Oracle 的)。
不适用场景
- 只需要创建单一产品(比如只需要创建手机,不需要平板、电脑),用抽象工厂会过度设计,增加不必要的接口和类,不如工厂方法简洁。
- 需要频繁扩展单一产品等级结构(比如在「手机、平板、电脑」的产品族中,频繁新增其他电子产品),此时需要修改抽象工厂接口,违反「开闭原则」,所有具体工厂都需要修改,维护成本极高。
- 产品之间无依赖关系(比如手机和充电器无需配套,可随意搭配),无需保证兼容性,用抽象工厂会增加复杂度,不如工厂方法灵活。
扩展阅读:
PlatformTransactionManager是 Spring 事务管理的顶层接口,也是抽象工厂模式的应用,核心拆解:
- 抽象工厂接口:
PlatformTransactionManager(定义了创建「事务产品族」的方法,getTransaction()创建事务状态、commit()提交事务、rollback()回滚事务)。- 具体工厂类:
DataSourceTransactionManager(JDBC/MyBatis 事务工厂,创建「JDBC 事务产品族」)、HibernateTransactionManager(Hibernate 事务工厂,创建「Hibernate 事务产品族」)。- 产品族:「JDBC 事务产品族」(包含
DataSourceTransactionManager、TransactionStatus、Connection)、「Hibernate 事务产品族」(包含HibernateTransactionManager、TransactionStatus、Session)。- 抽象工厂的体现:每个具体工厂(
DataSourceTransactionManager)创建一个「事务产品族」,保证事务相关产品的兼容性,扩展新的事务类型(如 Redis 事务)只需新增PlatformTransactionManager实现类。
二、 总结对比表
| 特性 | 简单工厂 | 工厂方法 | 抽象工厂 |
|---|---|---|---|
| 创建对象数量 | 一个 | 一个 | 一系列 (产品族) |
| 复杂度 | 低 | 中 | 高 |
| 开闭原则 | 违背 (需改代码) | 符合 (只增类) | 倾斜 (加产品族容易,加新产品难) |
| 核心特点 | 用于创建逻辑简单的对象 | 用于解耦创建与使用 | 用于保证产品组合的一致性 |
| 常见程度 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐ |
三、 谁是“策略模式”的最佳拍档?
答案是:简单工厂 (Simple Factory) 或 它的进化版(Map 注册表)。
为什么?
- 策略模式 负责定义“怎么做”(算法/行为)。
- 工厂模式 负责解决“用哪个”(创建/选择)。
在实际业务中,我们通常需要根据前端传来的 type(比如 “ALI_PAY”)来找到对应的策略。这正是简单工厂最擅长的场景:根据参数返回实例。
实战中的“混合体”
在 Spring 中,我们很少写原始的 if-else 简单工厂,而是使用 “Map 注入 + 简单工厂” 的方式(就是策略模式笔记中的 PaymentFactory)。
- 它的外壳:看起来像工厂(提供
getStrategy(type)方法)。 - 它的内核:利用了 Spring 的自动注册机制,既避免了
if-else(简单工厂的缺点),又避免了写无数个工厂类(工厂方法的缺点)。
结论:在 Spring 环境下,策略模式 + (Map版) 简单工厂 是消除 if-else 的终极杀招。
四、 什么时候用抽象工厂?
你在做业务系统(CRUD)时,99% 的情况用不到抽象工厂。
抽象工厂通常出现在 中间件开发 或 跨平台框架 中:
- JDBC:这是典型的抽象工厂。当你连接 MySQL 时,Connection、Statement、ResultSet 这一整套都是 MySQL 驱动提供的;当你换成 Oracle 时,这一整套都要换成 Oracle 的实现。你不能用 MySQL 的 Connection 去创建 Oracle 的 Statement。
- UI 库 (AWT/Swing):在 Windows 下,按钮和窗口是 Windows 风格的;在 Linux 下,是 GTK/KDE 风格的。工厂负责生产这一整套 UI 组件。
总结
- 做业务:用 简单工厂(配合 Map)来管理策略或 Bean。
- 写框架/库:如果你的框架需要支持多套底层实现(如支持多种数据库、多种操作系统),才考虑 抽象工厂。
- 一般解耦:如果仅仅是为了把
new拿出去,用 工厂方法。
更多推荐




所有评论(0)