什么是抽象工厂模式?它解决什么问题?
简化版
抽象工厂模式提供一个创建一组相关对象的接口,让调用方不依赖这些对象的具体类。它主要解决产品族的一致创建问题,避免系统里混用不兼容的一组产品。
详细版
抽象工厂的核心是“创建一整套相关产品”。例如跨平台 UI 中,Windows 风格需要 WindowsButton、WindowsCheckbox,Mac 风格需要 MacButton、MacCheckbox。调用方只依赖 UIFactory:
interface UIFactory {
Button createButton();
Checkbox createCheckbox();
}
class WindowsFactory implements UIFactory {
public Button createButton() {
return new WindowsButton();
}
public Checkbox createCheckbox() {
return new WindowsCheckbox();
}
}
业务代码拿到某个具体工厂后,就能创建同一产品族下的一组对象。这样可以保证 Windows 按钮和 Windows 复选框成套出现,Mac 组件也成套出现。
它适合产品族稳定、产品族之间需要整体切换的场景;不适合产品等级经常新增的场景,因为新增一个产品等级通常要修改抽象工厂接口和所有具体工厂。
完整版教学
一、抽象工厂的关键词是产品族
很多人会把抽象工厂理解成“更复杂的工厂”,这不够准确。它真正强调的是一组相关对象的创建一致性。
产品族可以理解为同一主题、同一平台、同一协议、同一厂商下的一套产品。比如 Windows UI 组件是一族,Mac UI 组件是一族;阿里云存储、队列、短信客户端可以是一族,AWS 对应客户端也是一族。
二、模式结构包括四类角色
抽象工厂模式通常包括:
- 抽象工厂:声明创建一组产品的方法;
- 具体工厂:创建某个产品族下的具体产品;
- 抽象产品:声明每类产品的公共能力;
- 具体产品:属于某个产品族的具体实现。
抽象工厂本身不直接执行业务逻辑,它负责把一组产品的创建规则收拢到同一个入口。
三、它解决的是混搭风险
如果调用方到处自己 new,就可能出现 WindowsButton 搭配 MacCheckbox 这种风格混乱的问题。抽象工厂让调用方先选择工厂,再通过同一个工厂创建整套产品,从结构上减少混搭。
这类问题在 UI、数据库方言、云厂商适配、消息中间件适配中都很常见。
四、它的代价是接口稳定性要求高
抽象工厂很适合新增产品族,比如新增 LinuxFactory。但如果要新增产品等级,比如 createMenu(),就要修改 UIFactory,并让所有具体工厂实现这个方法。
所以抽象工厂适合“产品族常扩展、产品等级相对稳定”的系统。
五、从“混搭会出错”推导模式结构
如果 WindowsButton 能与 MacCheckbox 任意组合且没有差异,抽象工厂价值有限。只有产品之间存在主题、协议、配置或兼容约束,混搭会造成错误时,选择一个 Factory 并从中取得整套产品才真正解决问题。
客户端 -> UIFactory
WindowsFactory -> WindowsButton
WindowsFactory -> WindowsCheckbox
MacFactory -> MacButton
MacFactory -> MacCheckbox
客户端只保存一个 Factory
同一 Factory 的多个 create 方法组成一族
具体产品类对客户端不可见
矩阵的每一行是一族兼容产品,每一列是一个产品等级。客户端选择一行对应的具体工厂,再从该工厂取得各列产品;如果允许调用方绕过工厂分别选择每个具体类,产品族一致性的结构性保证就消失了。
六、二维扩展成本与模式边界
| 评审维度 | 本题结论 |
|---|---|
| 产品族不变量 | 一次上下文中所有相关产品都由同一个具体工厂创建。 |
| 新增一行(产品族) | 新增一套具体产品和具体工厂,客户端仍依赖抽象。 |
| 新增一列(产品等级) | 修改抽象工厂和所有具体工厂,是模式的主要代价。 |
| 不应由工厂承担 | 产品运行逻辑、跨对象业务编排和全局依赖查询。 |
| 退回更轻方案的条件 | 一个产品等级用工厂方法;无混搭风险或实现固定时直接注入。 |
抽象工厂对所有变化并非都开放。它刻意固定列集合,以换取整行切换的一致性;如果系统最常发生的是新增列,工厂接口和所有行实现会频繁联动,此时应拆分工厂边界或选择组合方式。
七、落地验收清单
- 为每个具体工厂跑同一组契约测试,确认每个创建方法都返回正确产品等级。
- 给产品暴露
familyId()仅用于测试,校验同一工厂创建的所有产品 familyId 一致。 - 加入一个假产品族,记录是否只新增一行实现和装配配置,而未修改旧工厂。
- 模拟新增一个产品等级,评估抽象接口和全部具体工厂的真实修改成本。
- 检查公共产品接口是否出现 AWS、MySQL、Windows 等具体实现词汇。
- 检查具体工厂是否只做组装与创建,没有吞入完整业务流程。
- 若产品持有连接或线程池,明确由工厂、容器还是调用方负责关闭。
- 若运行期按租户切换工厂,验证路由缓存、凭证刷新和租户隔离。
记忆钩子:抽象工厂不是“更抽象的 new”,而是“一次选择,整套一致”。
八、常见误区与追问
- 误区:抽象工厂主要用于创建复杂单个对象。 复杂单对象分步构建更接近 Builder。
- 误区:产品族只是多个同类实现。 同类实现构成产品等级,产品族跨越多个产品等级。
- 误区:客户端可以知道具体产品类。 理想结构下客户端依赖抽象产品和抽象工厂。
- 追问:模式有哪些角色? 抽象/具体工厂与抽象/具体产品四组角色。
- 追问:为什么防止混搭? 客户端只选择一个具体工厂,各创建方法都返回该族实现。
- 追问:适用的变化模式是什么? 产品族经常增加或整体切换,而产品等级集合相对稳定。
九、加强记忆
抽象工厂不是创建一个对象,而是创建一整套相关对象。看到“同一平台、同一风格、同一厂商的一组对象不能混搭”,就可以优先想到抽象工厂模式。