进行 iOS 开发时,你可能曾在别人的代码中看到 init(...) 而不是 static func make(...),感到疑惑。
先说结论。
Swift 静态工厂方法可以为创建流程命名,灵活调整返回类型,还能控制对象复用;当只用 init 不够灵活时,它就很有用。
今天从实践角度逐一说明为什么用 static func make 代替 init。
本文的静态工厂只是名称上类似 GoF Factory Method Pattern;该模式由子类决定创建方式。更多 Swift 实现示例见 使用工厂函数分离创建逻辑的方法,GoF 模式的边界见 工厂方法 vs. 抽象工厂。
静态工厂方法到底是什么?
名字听起来很复杂,但概念很简单。
这是一个创建并返回对象的 static 方法。不直接调用构造器,而是用一个函数封装创建过程。
大致如下。
struct Button {
let title: String
let style: Style
// init外包一层的静态工厂方法
static func makePrimary(title: String) -> Button {
Button(title: title, style: .primary)
}
}
调用处会是这样:Button.makePrimary(title: "확인")。
结果与直接调用 Button(title:style:) 相同,只是从名称就能看出“创建的是哪个按钮”。
这个小差异在实际代码中影响不小。
用 make 代替 init 的 4 个理由
下面是我在实际项目中感受到的优点。
1. 用名称表达意图
构造器的名称都一样,都是 init,只能通过参数组合区分。
因此,类似的 init 一多就容易混淆。使用 make(fromJSON:)、make(withDefaults:) 这样的名称,只看调用处也能知道创建的是什么。
2. 不必每次都创建新对象
每次调用 init 都会创建新实例。
而工厂方法可以返回缓存对象或已有的单例。对于 Bool 的真值和假值这类固定值,复用效率高得多。
3. 返回类型更加灵活
我个人认为这是最强大的优点。
工厂方法可以返回声明类型的子类型或协议实现。调用方不需要知道具体类型。
protocol Shape { func area() -> Double }
enum ShapeFactory {
// 根据条件返回不同实现
static func make(sides: Int) -> Shape {
sides == 4 ? Square() : Triangle()
}
}
make(sides:) 的返回类型只有一个 Shape,但实际会返回符合当前情况的实现。
4. 可以更平滑地处理失败
虽然可以使用 init? 或 throws,但工厂方法很方便直接返回可选值,或用 Result 包装结果。创建逻辑复杂时,流程会更清晰。
和 init 有什么不同?一眼看懂的对比
为了避免混淆,我整理成了表格。
| 分类 | init(构造器) | static func make(静态工厂) |
|---|---|---|
| 名称 | 始终固定为 init | 可自由指定 |
| 新实例 | 每次强制创建 | 支持复用和缓存 |
| 返回类型 | 只能是自身类型 | 可以是子类型或协议 |
| 失败处理 | init? / throws | 可选值、Result 等均可灵活使用 |
| 缺点 | 名称无法区分情况 | 子类化受限,发现性较低 |
也坦诚说说它的缺点。
如果只有工厂方法而没有 public init,就很难通过继承扩展该类型。
构造器会直接出现在自动补全中,但查找 make 通常需要知道名称,因此发现性稍低。建议使用 make、create、from 这类约定名称。
那么,什么时候该用它?
我在实践中的判断标准如下。
- 只需填充存储属性 → 直接用
init - 创建方式有多种,想通过名称区分 →
make - 需要根据条件返回不同类型 →
make - 想复用或缓存对象 →
make
总之,当创建不再只是“填充值”,而成为一个决策过程时,工厂方法就能发挥作用。
反过来,把所有创建都封装成 make 反而会让代码冗长。关键是只在需要时使用。
一开始可能不习惯,但理解意图后,阅读代码会变得更有趣。下次遇到类似情况,不妨试试 make,相信会有帮助 🙂

