使用 Swift 开发 App 时,初始化代码不知不觉就会变成这样。
User(name:age:email:phone:address:isVerified:profileURL:createdAt:)
括号里有 8 个参数。
查看调用方代码时,你不得不逐个数值,才能确认哪个是 email、哪个是 phone。
这在 side project 中也很常见,但用 Swift Builder Pattern 整理后就能干净地解决。
先说结论,对于参数很多的初始化,将其改为用 Builder Pattern“每行设置一个参数”,可读性会大幅提升。不必记住顺序,也不用勉强用 nil 填充未使用的值。
先总结一下你能从本文中获得的内容。
- 为什么 8 个参数的 init 容易引发错误
- Builder Pattern 如何解决这个问题
- Swift 中实际使用的代码形式
- 什么时候使用、什么时候避免这个模式
8 个参数的 init 为什么像地狱?
参数只有三四个时,其实没有什么大问题。
问题从参数数量增加时开始。
第一,容易弄错顺序。即使把 phone 放到 email 的位置,只要两者都是 String,编译器也不会报错。
第二,可选值的处理会变得杂乱。就连当前不用的值,也必须用 nil, nil, nil 填上。
第三,调用方变得很长,很难一眼看出正在创建什么对象。
参数越多,init 就越像一张“需要填满空白的试卷”;空白越多,错误也越多。
把 phone 和 profileURL 的位置放反,可能会让你排查一天以上。类型相同时,这类错误真的很难察觉。
什么是 Builder Pattern?
Builder Pattern 不会一次性创建复杂对象,而是逐个填入值,最后完成对象的创建。
它不是在初始化时传入所有值,而是选择并设置需要的值,最后通过类似 build() 的方法获取最终对象。
想象一下在餐厅点餐就容易理解了。不是一次喊出所有食材,而是逐项说:“面包要全麦,奶酪加量,不要酱汁。”
如果设置关键值的方法返回自身,就可以像连接点号一样进行链式调用。下面是最简单的 builder 形式。
final class UserBuilder {
private var name = ""
private var email: String?
// 设置一个值并 self返回它,从而支持链式调用
func setName(_ v: String) -> Self { name = v; return self }
func setEmail(_ v: String) -> Self { email = v; return self }
func build() -> User { User(name: name, email: email) }
}
由于设置方法返回 Self,因此可以像 .setName(...).setEmail(...) 一样继续调用。
Swift 中如何使用 Builder Pattern?
你最想知道的,应该就是实际调用方会如何变化。
使用前面创建的 builder 后,对象创建代码会变成这样。
// 只设置需要的值,添加名称标签,无需担心顺序
let user = UserBuilder()
.setName("李锡宇")
.setEmail("[email protected]")
.build()
// 不必给未使用的值填入 nil
如你所见,不需要再考虑顺序。方法名本身就是标签,从结构上避免了把 phone 传到 email 位置这类错误。
不用的值只要不调用对应方法即可。
在 Swift 中,也常用 struct 而不是 class 来实现 builder,或者传入 closure,在一个代码块中完成设置。根据项目风格选择即可。
Builder Pattern 什么时候该用,什么时候该避免
只因为看起来不错就到处使用,反而只会增加代码量。
我把自己的判断标准整理成了表格(基于截至 2026 年的个人项目经验)。
| 情况 | 建议 |
|---|---|
| 5 个以上参数,可选值很多 | Builder Pattern |
| 有多个相同类型的参数 | Builder Pattern |
| 2~3 个参数,全部必填 | 普通 init |
| 值简单且固定 | 普通 init |
总的来说,如果参数少且全部必填,就没有必要专门创建 builder。
相反,如果是一个混合了多个可选值的大型对象,builder 确实更方便。
问:创建 builder 后,代码不会变得更多吗?
没错。builder 类本身会增加代码。不过,如果这个对象要在多个地方创建,调用方变得整洁所带来的收益更大。
问:Swift 有默认参数,不能直接用它吗?
参数只有三四个时,只使用默认参数就够了。参数很多且组合多样时,Builder Pattern 的优势才会体现出来。
如果你正因 8 个参数的 init 而感到压力,不妨只在这个位置尝试 Builder Pattern。不必全部推倒重来,从最复杂的一个对象开始,负担会小很多。祝你编码愉快!

