抽象工厂和工厂方法有什么区别?
简化版
工厂方法通常负责创建一种产品,抽象工厂负责创建一组相关产品。前者解决单条产品线的扩展问题,后者解决多条产品线在同一产品族下的一致创建问题。
详细版
两者的区别可以这样看:
| 维度 | 工厂方法 | 抽象工厂 |
|---|---|---|
| 创建目标 | 一种产品 | 一组相关产品 |
| 抽象接口 | 通常一个创建方法 | 通常多个创建方法 |
| 扩展优势 | 新增同类产品 | 新增产品族 |
| 典型场景 | 不同 Parser、不同 Sender | 跨平台 UI、云厂商 SDK 套件 |
工厂方法例子:
interface ParserFactory {
Parser createParser();
}
抽象工厂例子:
interface CloudFactory {
StorageClient createStorage();
QueueClient createQueue();
}
如果系统只创建一种对象,用工厂方法更轻;如果系统要创建一套互相匹配的对象,用抽象工厂更合适。
完整版教学
一、先看创建对象的数量和关系
如果要创建的对象只有一种,比如报文解析器、消息发送器、日志写入器,重点是这一类产品内部怎么扩展,工厂方法就够用。
如果要创建的是一组对象,并且这些对象必须来自同一套上下文,抽象工厂更合适。
这里的“一组”不是简单指一次创建多个实例,而是多个产品等级之间存在必须共同切换的兼容关系。即使一次创建十个 Parser,它们仍属于同一产品等级,通常还是工厂方法问题。
二、工厂方法是抽象工厂的组成基础
抽象工厂里的每个创建方法都可以看作一个工厂方法。例如 createButton() 创建 Button 产品等级,createCheckbox() 创建 Checkbox 产品等级。
区别在于抽象工厂把多个创建方法放在同一个抽象工厂接口里,用来表达产品族的一致性。
三、扩展方向不同
工厂方法新增产品通常新增一个具体产品和具体工厂。抽象工厂新增产品族通常新增一个具体工厂,并实现所有产品等级的创建方法。
但抽象工厂如果新增产品等级,就会比较麻烦,因为所有具体工厂都要补新方法。
四、不要用复杂度更高的模式解决简单问题
如果系统没有产品族概念,只是创建一种产品,强行使用抽象工厂会让接口显得臃肿。设计模式要跟变化点匹配,而不是越复杂越好。
判断时可以尝试画产品矩阵:只有一列就优先工厂方法,至少两列且每一行必须保持兼容才考虑抽象工厂。这个检查能避免因为接口名称里出现 Factory 就机械升级模式。
五、一列和多列矩阵对应不同模式
工厂方法可看成只关注矩阵的一列:例如 Parser 的多种实现。抽象工厂把 Parser、Validator、Serializer 多列放入同一行工厂,目的不是创建数量更多,而是让同一行的实现保持兼容。
工厂方法:
Parser
JSON族 JsonParser
XML族 XmlParser
抽象工厂:
Parser Validator Serializer
JSON族 JP JV JS
XML族 XP XV XS
矩阵的每一行是一族兼容产品,每一列是一个产品等级。客户端选择一行对应的具体工厂,再从该工厂取得各列产品;如果允许调用方绕过工厂分别选择每个具体类,产品族一致性的结构性保证就消失了。
六、二维扩展成本与模式边界
| 评审维度 | 本题结论 |
|---|---|
| 产品族不变量 | 工厂方法维持单个产品抽象;抽象工厂维持一组产品的族一致性。 |
| 新增一行(产品族) | 工厂方法新增具体 Creator;抽象工厂新增实现所有列的具体 Factory。 |
| 新增一列(产品等级) | 工厂方法仍是一列内扩展;抽象工厂需改接口和每个族。 |
| 不应由工厂承担 | 把无兼容关系的多个产品方法拼到一个接口。 |
| 退回更轻方案的条件 | 只有一种产品等级时选工厂方法,避免抽象工厂过重。 |
抽象工厂对所有变化并非都开放。它刻意固定列集合,以换取整行切换的一致性;如果系统最常发生的是新增列,工厂接口和所有行实现会频繁联动,此时应拆分工厂边界或选择组合方式。
七、落地验收清单
- 为每个具体工厂跑同一组契约测试,确认每个创建方法都返回正确产品等级。
- 给产品暴露
familyId()仅用于测试,校验同一工厂创建的所有产品 familyId 一致。 - 加入一个假产品族,记录是否只新增一行实现和装配配置,而未修改旧工厂。
- 模拟新增一个产品等级,评估抽象接口和全部具体工厂的真实修改成本。
- 检查公共产品接口是否出现 AWS、MySQL、Windows 等具体实现词汇。
- 检查具体工厂是否只做组装与创建,没有吞入完整业务流程。
- 若产品持有连接或线程池,明确由工厂、容器还是调用方负责关闭。
- 若运行期按租户切换工厂,验证路由缓存、凭证刷新和租户隔离。
记忆钩子:工厂方法看一列怎么扩展,抽象工厂看一行怎样成套。
八、常见误区与追问
- 误区:抽象工厂只是工厂方法返回多个对象。 关键是多个产品等级组成相关产品族。
- 误区:工厂方法和抽象工厂互斥。 抽象工厂的每个创建方法都可视为一个工厂方法。
- 误区:创建两个实例就必须抽象工厂。 数量不重要,关系和成套切换才重要。
- 追问:如何选择模式? 先数产品等级,再判断不同等级是否必须来自同一族。
- 追问:抽象工厂新增族为何容易? 现有创建方法稳定,新具体工厂实现整套方法即可。
- 追问:能否把大抽象工厂拆开? 可以按稳定边界拆成多个小工厂,再由组合对象聚合。
九、加强记忆
工厂方法是一条线,抽象工厂是一张网。只有当多种产品需要成套创建、整体切换时,抽象工厂才真正发挥价值。