14、工厂模式对比:三种工厂模式的适用场景对比表、如何选择
讲完了三种工厂模式的具体实现,你可能会问:「我到底该用哪一种?」
说实话,这个问题我当年也纠结了很久。记得刚带团队那会儿,有个同事把简单工厂和工厂方法混着用,结果项目后期扩展新类型时,改得那叫一个痛苦。后来我花了整整一个下午,把三种模式的适用场景和取舍逻辑理了一遍,才真正搞明白。
今天我就把这份经验直接给你。咱们不绕弯子,直接上对比表,再聊聊怎么选。
三种工厂模式的核心区别
先看一张我亲手画的对比图,把三种模式的结构和决策点说清楚。
适用场景对比表
下面这张表我整理了很久,把三种模式从六个维度做了对比。你直接拿去用,面试或做技术选型时都能派上用场。
| 对比维度 | 简单工厂 | 工厂方法 | 抽象工厂 |
|---|---|---|---|
| 核心思想 | 一个工厂类集中创建 | 每个产品一个工厂子类 | 创建一组相关产品 |
| 扩展性 | 差(改代码) | 好(加类) | 好(加产品族) |
| 产品数量 | 少(<5种) | 中等(5-20种) | 产品族多 |
| 代码复杂度 | 低 | 中 | 高 |
| 开闭原则 | 违反 | 符合 | 符合(产品族维度) |
| 典型场景 | 日志记录器、简单数据源 | 多种数据库驱动、文档解析 | 跨平台UI组件、主题系统 |
我的经验之谈: 别一上来就上抽象工厂。我见过太多项目,明明只需要两三种产品,硬是搞出四五个工厂接口,最后维护成本比写死还高。记住,简单工厂不是坏模式,滥用抽象工厂才是坑。
如何选择?三个决策步骤
你不需要背表格。我教你一个三步决策法,每次做选型时走一遍就行。
- 数一数产品种类 —— 如果少于5种,且未来一年内不太可能增加,直接上简单工厂。别犹豫。
- 看看扩展方向 —— 如果产品种类会持续增加(比如要支持新的数据库、新的文件格式),选工厂方法。它最符合开闭原则。
- 判断是否有产品族 —— 如果产品之间有关联,必须一起使用(比如Windows下的按钮+文本框+菜单),那就用抽象工厂。否则别碰。
一个小技巧: 当你拿不准时,先从简单工厂开始。等发现每次新增产品都要改工厂类时,再重构为工厂方法。这叫「渐进式设计」,比一开始就过度设计要靠谱得多。
避坑指南:我曾经踩过的三个坑
这些年我见过不少团队在工厂模式上翻车。说几个典型的,你引以为戒。
- 坑一:简单工厂里塞了30种产品 —— 那个switch语句长得像裹脚布。每次加新产品都要改工厂,改一次测一次,烦不胜烦。后来我重构成了工厂方法,世界清净了。
- 坑二:抽象工厂里硬塞不相关的产品 —— 有人把日志记录器和数据库连接放在同一个产品族里。你想想看,这俩东西根本不需要一起创建,强行耦合只会让代码更乱。
- 坑三:为了用模式而用模式 —— 有个项目只有两种数据库,却搞了四个工厂类。我接手时差点崩溃。记住,模式是工具,不是目的。
警告: 如果你发现自己的工厂类里出现了大量的 if-else 或 switch,并且每个分支的代码超过10行,那就是重构的信号。别等到代码发臭再动手。
一个真实案例:从简单工厂到工厂方法
我之前参与过一个报表系统,最初只支持PDF导出。用简单工厂,一个 ReportFactory 搞定。
// 简单工厂版本
public class ReportFactory {
public Report create(String type) {
if ("pdf".equals(type)) return new PdfReport();
// 后来加了 Excel、Word、HTML...
// 每次都要加 else if,烦死了
}
}
后来需求爆炸,要支持Excel、Word、HTML、CSV……我果断重构为工厂方法。每个报表类型一个工厂子类,新增类型只需加一个类,不改原有代码。
// 工厂方法版本
public interface ReportFactory {
Report create();
}
public class PdfReportFactory implements ReportFactory {
public Report create() { return new PdfReport(); }
}
public class ExcelReportFactory implements ReportFactory {
public Report create() { return new ExcelReport(); }
}
// 新增时只需加新工厂类,不用改旧代码
这个改动让团队后续半年加了5种新报表,没有一次因为工厂代码出过bug。嗯,这就是模式的力量。
总结一句话
简单工厂图省事,工厂方法图扩展,抽象工厂图产品族。选哪个,取决于你的需求是「稳定」还是「变化」,是「单个产品」还是「产品家族」。
我个人习惯:能用简单工厂解决的问题,绝不用工厂方法;能用工厂方法解决的问题,绝不上抽象工厂。保持简单,保持可控。
公众号:蓝海资料掘金营,微信deep3321