链式调用是 fluent builder 的必要手段,但 builder 模式的核心在于构造逻辑分离、终态校验集中和 product 不可变;builder 方法必须返回 this,build() 唯一出口负责校验与创建 product,避免中间校验与状态丢失。

链式调用本身不等于建造者模式,但它是实现 Fluent Builder 的必要手段;真正决定是否是 Builder 模式的,是构造逻辑的分离、终态校验的集中、以及 Product 的不可变性。
Builder 类必须返回 this,不能返回新实例或 void
这是链式调用能成立的底层前提。返回 void 会中断调用链;返回新实例(比如 new PizzaBuilder())会导致前面设置的状态丢失——因为新对象没继承旧对象的字段值。
-
WithSize("large")必须返回this(即当前PizzaBuilder实例的引用),不是new PizzaBuilder() - C# 中推荐用
public PizzaBuilder WithSize(string size),而非void或泛型返回类型——后者会干扰类型推导和 IDE 自动补全 - 如果 Builder 内部状态含可变集合(如
List<string></string>),注意不要在WithXxx()中重新 new 它,否则历史数据清空
Build() 是唯一出口,所有校验和副作用必须放在这里
很多人把参数校验提前到每个 SetXxx() 方法里,结果导致错误堆栈分散、重复校验、甚至构建中途抛异常后无法继续链式调用。正确做法是延迟校验,只在 Build() 中一次性检查完整状态。
- 例如:URL 字段为空、超时值为负数、必填 toppings 数量不足——这些都该在
Build()里统一判断并 throwInvalidOperationException -
Build()还应负责创建最终 Product 实例。Product 类建议用私有构造 + 只读属性,避免外部绕过 Builder 直接 new - 不要在 Builder 中持有 Product 引用并逐步修改它;而是把所有字段收集好,在
Build()里 new 一次传参完成
Director 类多数时候是多余的一层
除非你有多个构建流程复用同一组步骤(比如“测试环境跳过认证,预发环境启用缓存,生产环境全开”),否则硬加 Director 只会让调用更啰嗦:先 new Director,再传 Builder,再调 Construct(),最后还要从 Director 拿结果。
- 简单场景直接链式调用:
new ApiClientBuilder().SetUrl("https://api.example.com").SetTimeout(5).SetRetry(3).Build() - Director 真正有价值的地方,是封装条件分支逻辑,且这些逻辑被三处以上共享。此时它应只依赖抽象
IBuilder,不感知具体类型 - 如果构建步骤固定、组合极少,加 Director 反而增加维护成本和理解门槛
Fluent Builder 最容易崩在 null 引用,但错误堆栈不提示哪步出错
链式调用看着顺滑,但一旦某步初始化失败(比如 JSON 配置解析失败导致 _toppings 仍为 null),后续 AddTopping() 就会直接抛 NullReferenceException,堆栈里只显示在 AddTopping 行,根本看不出是上一步没初始化好。
- 所有可空字段(如集合、嵌套 builder)必须在构造函数里初始化,别等第一次调用才 lazy 创建
- 避免在
WithXxx()中做重操作(如远程拉配置、IO 解析),这些应该前置或移到Build()里统一处理 - 如果真需要中间校验,可用
TrySetXxx()+ 返回布尔值替代,但会破坏链式结构——这时就得权衡可读性与健壮性
最常被忽略的点是:Builder 不是语法糖,它是控制对象生命周期和约束合法状态的边界。写得越“顺手”,越要警惕状态漂移和校验遗漏。










