首页/开发/代码质量与评审速查/工厂方法 vs 抽象工厂:何时再升一层
设计原则与模式62 条

工厂方法 vs 抽象工厂:何时再升一层

工厂方法创建单一产品;抽象工厂创建相互关联的一族产品:当出现「同一主题的多个产品必须配套」时再升抽象工厂,否则单产品工厂足够。

一个创建点还是一族创建点

工厂方法解决「创建一个产品」的问题:把选择的决策收进一个创建点,让调用方依赖接口而不是具体类。抽象工厂解决「创建一族相互配套的产品」:选定某个主题后,必须同时使用该主题下的整组产品。当配套约束出现,就该把多个创建点收进同一个工厂接口。

基本信息

单产品工厂方法或静态工厂,只有一个创建点
产品族抽象工厂,一组必须配套的创建点
升级信号配套约束:选了甲主题就必须同时使用甲的附件
代价产品种类增加时,所有工厂实现都要同步修改

典型使用场景

  • 界面主题:同一套皮肤的按钮、输入框、图标必须配套,避免混搭
  • 跨平台适配:不同操作系统的控件族整体切换
  • 数据库方言:连接、命令、参数处理与分页语法成套切换
  • 测试替身:用整套桩对象替换生产实现,保证内部一致
  • 云服务封装:对象存储、消息队列、密钥管理成套切换供应商

两种工厂的形态

两种工厂的形态
# 工厂方法:一个创建点,调用方依赖接口
class LoggerFactory:
    def create(self) -> Logger: ...

# 抽象工厂:一族配套产品,选主题即选全套
class UiFactory:                      # 抽象工厂接口
    def button(self) -> Button: ...
    def checkbox(self) -> Checkbox: ...
    def icon(self) -> Icon: ...

class DarkUiFactory(UiFactory): ...   # 深色主题全套实现
class LightUiFactory(UiFactory): ...  # 浅色主题全套实现

什么时候该再升一层

判断问题用工厂方法用抽象工厂
产品之间是否需要配套约束不需要,各自独立创建需要,选主题即选全套
产品种类数量单一或很少,长期稳定成族出现,多个维度
新增产品的影响面改一个创建点即可所有工厂实现都要改
抽象工厂的接口一旦定死,扩产品种类的成本很高:所有实现类都要加方法。可以用接口默认方法给新方法一个临时默认实现避免全量改动,但更稳妥的做法是按能力拆小接口或分组。
静态工厂只是一个返回实例的静态方法,不涉及多态扩展。它常被当作工厂方法使用,在需要按配置切换实现时会形成长串条件分支,此时再引入多态工厂,收益才明确。

易错点

  • 产品不需要配套却用抽象工厂:接口面空转,多出一层没人受益的抽象
  • 产品族频繁扩种仍不拆接口:所有工厂被迫同步修改,改一次动全局
  • 用字符串或枚举在调用方硬编码选择工厂:主题元数据散落各处,新增主题要改多处
  • 抽象工厂返回共享可变对象:跨请求共享导致状态串台,应返回新实例或不可变对象

数据来源与参考

  • 《设计模式》(GoF)· Factory Method 与 Abstract Factory 对比
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌