建造者模式(Builder):复杂对象的"分步构造说明书"
更新时间:2026-08-25。本文按"先看不用模式的困境 → 再用模式重构 → 类图对比分析"的结构,论述建造者模式。可运行代码:demos/design-patterns/builder。
一、问题场景:构造一个"部件很多、可选参差不齐"的对象
HTTP 请求(url、method、headers、body、超时、重试……)、数据库连接配置、游戏角色(种族、职业、属性、装备、技能点)。对象由大量可选部件组成,且不同配置组合差异巨大。
二、不使用模式:构造参数爆炸 vs setter 泛滥
方案 A:构造函数参数爆炸
cpp
// 第 4 个参数起全是可选参数——调用方要背参数顺序
Request r("GET", "/api", "", "", 5000, true, 3, false, nullptr, "gzip");| 缺陷 | 说明 |
|---|---|
| 可读性灾难 | 10 个 bool/int 参数,谁也不知道每个位置含义 |
| 可选参数无默认 | 调用方被迫为不用的参数写占位值 |
| 组合爆炸 | 参数加一个,所有构造调用点全要改 |
| 不可变对象难建 | 想保持不可变,构造函数会越来越长 |
方案 B:默认构造 + 散乱 setter
cpp
Request r; r.setUrl("/api"); r.setMethod("GET"); r.setTimeout(5000); ...| 缺陷 | 说明 |
|---|---|
| 中间态泄漏 | 构建中途对象处于"半成品"状态,可能被误用 |
| 一致性无保障 | 谁也不能保证 setter 被正确调用齐 |
| 不可变失效 | 对象必须可变,多线程下要处理 setter 竞态 |
不使用模式的类图

三、使用模式:建造者分步构建、最后一步"凝固"
建造者模式把"构造过程"与"产品"分离:Builder 提供分步方法,build() 最后校验并产出完整、可选不可变的产品;可选参数有默认值,调用顺序由链式调用自然引导。
cpp
// request_builder.h
class Request {
public:
Request(std::string url, std::string method, int timeout, int retry)
: url_(std::move(url)), method_(std::move(method)),
timeout_(timeout), retry_(retry) {}
// 只读访问器,不可变
private:
std::string url_, method_;
int timeout_, retry_;
};
class RequestBuilder {
public:
RequestBuilder& url(std::string v) { url_ = std::move(v); return *this; }
RequestBuilder& method(std::string v) { method_ = std::move(v); return *this; }
RequestBuilder& timeout(int v) { timeout_ = v; return *this; }
RequestBuilder& retry(int v) { retry_ = v; return *this; }
Request build() {
if (url_.empty()) throw std::invalid_argument("url required");
return Request(url_, method_, timeout_, retry_);
}
private:
std::string url_, method_;
int timeout_ = 3000; // 默认值集中在建造者
int retry_ = 0;
};
// 调用方:链式分步、只设关心的参数
Request r = RequestBuilder().url("/api").method("POST")
.timeout(5000).retry(3).build();使用模式的类图

四、类图对比与分析
| 维度 | 不使用(参数爆炸/setter) | 使用建造者 |
|---|---|---|
| 构造可读性 | 背参数顺序,全靠猜 | 链式命名方法,一目了然 |
| 可选参数 | 占位值/散乱 setter | 默认值集中在建造者 |
| 一致性 | 中途状态可能被误用 | build() 统一校验后产出 |
| 不可变性 | 难保证(setter 必须可变) | 产品可完全不可变 |
| 加新参数 | 改构造/全调用点 | 只加一个链式方法,向后兼容 |
| 性能 | 零额外开销 | 一次额外拷贝/搬移(可忽略) |
分析结论:建造者把"参数多"这个问题的阅读负担从调用方转移到可读的链式方法上,把"有效性"的检查收敛到 build() 一点。当对象有 5+ 个参数且多个可选时收益显著;参数少时不值得。
五、适用边界
| 适用 | 不适用 |
|---|---|
| 构造参数 ≥ 5 且大量可选 | 参数 ≤ 3 的简单对象(构造+默认值即可) |
| 参数间有组合约束(要统一校验) | 参数组合永远固定不变 |
| 想要不可变 + 可读构造 | 追求极简、团队不接受额外类 |
与工厂模式的区别:工厂关注"创建谁"(返回一族/一种完整产品);建造者关注"怎么逐步搭"(分步构建 + 最终 build)。复杂对象首选建造者,简单对象首选工厂或直接构造。
一句话总结
建造者模式用链式分步方法替代长参数构造,把默认值与有效性校验收敛到 build() 一点,产出完整且不可变的产品;是"多可选参数 + 组合约束"对象的可读构造方案,参数少时它带来的额外类不值得。