使用抽象工厂模式有哪些常见误区?
简化版
常见误区包括没有产品族也强行使用抽象工厂、把不相关产品塞进同一个工厂、让抽象接口泄漏具体实现细节,以及忽略新增产品等级的成本。抽象工厂的价值在于保证一组相关产品一致创建,不是为了把所有创建代码都集中到一个大工厂里。
详细版
高频误区可以归纳为五类:
- 没有产品族概念,只是创建一个对象,却套抽象工厂;
- 把不相关产品放进同一个工厂,导致接口越来越臃肿;
- 抽象产品接口暴露具体厂商或平台细节;
- 忽略新增产品等级会修改所有具体工厂;
- 具体工厂承担太多业务逻辑,变成流程编排器。
正确使用时,要先确认产品之间确实有关联,并且需要成套切换。比如云厂商存储和队列可以属于同一产品族,但用户服务、订单服务、支付服务通常不应简单放进同一个抽象工厂。
抽象工厂也不保证产品对象本身线程安全。它只约束创建来源和产品族一致性,生命周期、状态隔离和并发控制仍然要单独设计。
完整版教学
一、误区一:没有产品族也使用抽象工厂
如果系统只有一种产品,例如只创建 Parser,那么工厂方法或简单工厂就够了。抽象工厂需要多个相关产品等级,否则接口会显得空洞。
模式越重,越要有对应的复杂度来支撑。
二、误区二:产品族边界过宽
有些设计把所有对象创建都塞进一个 ApplicationFactory,里面有 createUserService()、createOrderDao()、createPaymentClient()。这种工厂没有清晰产品族,反而成了全局依赖入口。
好的抽象工厂应该围绕明确上下文组织,例如“某个 UI 风格”“某个数据库方言”“某个云厂商套件”。
三、误区三:抽象接口泄漏具体实现
如果公共接口里出现 setAwsRegion() 或 createMysqlLimitSql() 这类明显偏向某个具体实现的方法,说明抽象层被污染了。
应该把具体配置留在具体工厂内部,或者重新拆分更合适的抽象接口。
一旦泄漏,客户端就会出现 instanceof AwsStorage 或按厂商强转,整套替换只剩表面形式。修复时应回到业务真正稳定的能力交集,把厂商专属能力拆成可选接口或明确放在专属适配层。
四、误区四:把业务流程放进工厂
工厂负责创建对象,不负责完成业务流程。比如 CloudFactory 可以创建 StorageClient 和 QueueClient,但不应该把“上传文件、发送消息、写数据库”的完整业务流程都塞进去。
如果工厂开始编排业务,职责就偏离了创建型模式。
五、用“能否画出产品矩阵”识别是否滥用
如果候选设计只能画出 Parser 一列,没有第二类相关产品,就不存在需要成套保证的一行产品族。反过来,把 UserService、OrderDao、PaymentClient 拼成一行也不成立,因为它们没有共同的风格、平台或兼容约束。
Parser Validator
JSON族 JParser JValidator
XML族 XParser XValidator
每行产品共享协议/配置并必须兼容
若只有 Parser 一列 -> 工厂方法更轻
若行内对象无兼容关系 -> 不是产品族
抽象工厂只管理相关产品的创建
矩阵的每一行是一族兼容产品,每一列是一个产品等级。客户端选择一行对应的具体工厂,再从该工厂取得各列产品;如果允许调用方绕过工厂分别选择每个具体类,产品族一致性的结构性保证就消失了。
六、二维扩展成本与模式边界
| 评审维度 | 本题结论 |
|---|---|
| 产品族不变量 | 同一个具体工厂创建的产品必须来自同一真实上下文并能协同。 |
| 新增一行(产品族) | 新增一整套具体产品和一个具体工厂,旧行通常不改。 |
| 新增一列(产品等级) | 修改抽象工厂并让全部具体工厂补方法,影响面大。 |
| 不应由工厂承担 | 跨产品业务编排、万能依赖查询、产品内部算法。 |
| 退回更轻方案的条件 | 只有一个产品等级用工厂方法;对象无关联则用多个独立工厂。 |
抽象工厂对所有变化并非都开放。它刻意固定列集合,以换取整行切换的一致性;如果系统最常发生的是新增列,工厂接口和所有行实现会频繁联动,此时应拆分工厂边界或选择组合方式。
七、落地验收清单
- 为每个具体工厂跑同一组契约测试,确认每个创建方法都返回正确产品等级。
- 给产品暴露
familyId()仅用于测试,校验同一工厂创建的所有产品 familyId 一致。 - 加入一个假产品族,记录是否只新增一行实现和装配配置,而未修改旧工厂。
- 模拟新增一个产品等级,评估抽象接口和全部具体工厂的真实修改成本。
- 检查公共产品接口是否出现 AWS、MySQL、Windows 等具体实现词汇。
- 检查具体工厂是否只做组装与创建,没有吞入完整业务流程。
- 若产品持有连接或线程池,明确由工厂、容器还是调用方负责关闭。
- 若运行期按租户切换工厂,验证路由缓存、凭证刷新和租户隔离。
记忆钩子:画不出有意义的“行和列”,就不要使用抽象工厂。
八、常见误区与追问
- 误区:任何多个 create 方法都构成抽象工厂。 方法创建的产品还必须相关并形成可切换产品族。
- 误区:ApplicationFactory 把所有对象都放一起更统一。 没有产品族边界会演变成全局 Service Locator。
- 误区:抽象工厂会让新增任何对象都更容易。 新增产品等级恰恰会修改所有具体工厂。
- 追问:如何判断两个产品相关? 看它们是否共享平台、协议、主题或兼容约束并需要成套切换。
- 追问:具体工厂能做业务流程吗? 不应;它负责创建和组装,不负责执行跨产品用例。
- 追问:产品对象线程安全吗? 模式没有该保证,必须按各产品状态和生命周期单独设计。
九、加强记忆
抽象工厂最怕两个极端:没有产品族还硬套,或者产品族边界大到什么都装。判断它是否合理,就看这一组产品是否真的需要一致创建和整体切换。