软件设计

工厂方法与抽象工厂的区别

GoF 工厂方法将单个对象的具体类型决定交给子类,抽象工厂则一起创建相关对象产品族。本文通过 Swift 示例比较两种模式的意图和选择标准。

3 分钟阅读
工厂方法与抽象工厂的区别 封面图

学习设计模式时,总有一个让人至少卡住一次的阶段。

那就是工厂方法 vs 抽象工厂。两者名称相似,而且都能“代替我们创建对象”,确实很容易混淆。

这也是面试中的常见问题,所以我在本文中一次整理好了。

先说重点。工厂方法把对象要创建成哪个类交给子类决定;抽象工厂则一次性创建彼此相关的多个对象,也就是产品族。

static func make 这样为创建过程命名的静态工厂,与 GoF 工厂方法并不是一回事。如果想了解实际的 API 设计,建议先把它与 Swift 静态工厂方法 区分开。

一个,还是一整套?这就是最大的区别。

什么是工厂方法?

工厂方法是一种将对象创建责任从父类转交给子类的模式。

父类只规定“创建按钮”,具体创建哪种按钮由子类决定。

结合代码会更快理解。

protocol Button { func render() }

class Dialog {
    // 工厂方法:由子类决定创建什么
    func createButton() -> Button { fatalError("在子类中实现") }

    func render() {
        let button = createButton()
        button.render()
    }
}

class IOSDialog: Dialog {
    override func createButton() -> Button { IOSButton() }
}

Dialog 不需要知道自己使用哪种按钮。出现新平台时,只需再添加一个子类即可。

核心是继承。通过子类化来替换创建位置。


抽象工厂有什么不同?

抽象工厂是一种将相关对象绑定成一组来创建的模式。

它负责的不是一个按钮,而是按钮、复选框、滚动条等必须一起使用的对象。

protocol GUIFactory {
    func createButton() -> Button
    func createCheckbox() -> Checkbox
}

class IOSFactory: GUIFactory {
    func createButton() -> Button { IOSButton() }
    func createCheckbox() -> Checkbox { IOSCheckbox() }
}

class MacFactory: GUIFactory {
    func createButton() -> Button { MacButton() }
    func createCheckbox() -> Checkbox { MacCheckbox() }
}

关键在于一致性。使用 iOS 工厂时,按钮和复选框都会采用 iOS 风格,不会出现 iOS 按钮混入 Mac 复选框的问题。

抽象工厂通常从外部注入工厂对象后使用。它依靠组合,而不是继承来工作。

无论是 iOS 还是 Mac,只要替换工厂,整套产品就会一起改变
无论是 iOS 还是 Mac,只要替换工厂,整套产品就会一起改变

工厂方法通过继承解决“创建什么”,

抽象工厂则通过组合解决“创建哪一套”。


差异一览

只用语言解释容易混淆,所以整理成了表格。

分类 工厂方法 抽象工厂
目的 委托创建单个对象 创建相关对象族
基础 继承(子类化) 组合(组装)
创建内容 一种产品 多个产品(产品族)
扩展方式 添加新子类 添加新工厂
常见示例 createButton() 完整 GUIFactory

有意思的是,抽象工厂的内部实际上经常由工厂方法组成。与其说两者竞争,不如看作适用规模不同的模式。


何时使用,何时避免

实际工作中的判断标准比想象中简单。

  • 如果只需创建一种对象,工厂方法就够了。专门再创建工厂接口会属于过度设计。
  • 如果有两种以上的对象必须一起运行,可以考虑抽象工厂。
  • 如果产品族以后不会增加,很多时候直接使用条件分支更好。
情况 判断
只创建一个产品 工厂方法
产品作为一套一起运行 抽象工厂
产品族经常增加 抽象工厂
只需 2~3 个分支 不使用模式,采用简单分支

模式是工具,而不是目的。对不会扩展的代码提前加入抽象,只会让代码更难阅读。

模式只是工具,等需要时再使用也不迟
模式只是工具,等需要时再使用也不迟

面试时可以这样问

Q. 请说明工厂方法与抽象工厂的区别。 A. 工厂方法是基于继承、将单个对象的创建委托给子类的模式。抽象工厂则基于组合,以一致的方式创建相关对象族。核心区别在于创建一个对象还是一整套对象。

Q. 抽象工厂内部会使用工厂方法吗? A. 会,而且很常见。抽象工厂的每个创建方法,通常都会在内部以工厂方法的形式实现。两者不是对立概念,而是处理范围不同的模式。

现在只要看到这两个模式的名称,就能想到“一个还是一套”,说明你已经掌握重点了。面试前再快速读一遍本文,就会更有把握。