解释
生活中有着很多的Builder的例子,个人觉得大学生活就是一个Builder模式的最好体验:要完成大学教育,一般将大学教育过程分成 4 个学期进行,因此没有学习可以看作是构建完整大学教育的一个部分构建过程,每个人经过这 4 年的(4个阶段)构建过程得到的最后的结果不一样,因为可能在四个阶段的构建中引入了很多的参数(每个人的机会和际遇不完全相同)。
Builder 模式要解决的也正是这样的问题:当我们要创建的对象很复杂的时候(通常是由很多其他的对象组合而成),我们要要复杂对象的创建过程和这个对象的表示(展示)分离开来,这样做的好处就是通过一步步的进行复杂对象的构建,由于在每一步的构造过程中可以引入参数,使得经过相同的步骤创建最后得到的对象的展示不一样。
Builder模式核心作用:
分步创建复杂对象,特别适用于含大量可选参数或多步骤构造逻辑的对象
示例场景:
生成计算机配置单假设需要构造包含CPU/内存/存储等组件的计算机对象,各部件参数存在多种组合方式
问题代码(无Builder模式)
cpp
class Computer {
public:
// 构造函数臃肿且难以阅读
Computer(const string& cpu, int ram, int ssd,
bool hasGPU = false, string gpuModel = "",
bool hasCooler = false)
: cpu_(cpu), ram_(ram), ssd_(ssd),
hasGPU_(hasGPU), gpuModel_(gpuModel),
hasCooler_(hasCooler) {}
// 更糟糕的变种:多个重载构造函数
Computer(const string& cpu, int ram)
: Computer(cpu, ram, 512) {}
void showConfig() {
cout << "CPU: " << cpu_ << endl
<< "RAM: " << ram_ << "GB" << endl
<< "SSD: " << ssd_ << "GB" << endl
<< (hasGPU_ ? "GPU: " + gpuModel_ : "No GPU") << endl
<< (hasCooler_ ? "With Liquid Cooler" : "Standard Cooling") << endl;
}
private:
string cpu_;
int ram_;
int ssd_;
bool hasGPU_;
string gpuModel_;
bool hasCooler_;
};
// 客户端使用困难
int main() {
Computer highEndPC("i9-13900K", 64, 2000, true, "RTX4090", true); // 参数容易混淆
Computer midPC("i7-13700", 32); // 无法直观看出各参数对应关系
}
Builder模式解决方案
cpp
class Computer {
// 设为私有以防止直接实例化
Computer() = default;
public:
class Builder {
Computer computer_;
public:
Builder& setCPU(const string& cpu) {
computer_.cpu_ = cpu;
return *this;
}
Builder& setRAM(int ram) {
computer_.ram_ = ram;
return *this;
}
Builder& setSSD(int ssd) {
computer_.ssd_ = ssd;
return *this;
}
Builder& addGPU(const string& model) {
computer_.hasGPU_ = true;
computer_.gpuModel_ = model;
return *this;
}
Builder& addLiquidCooler() {
computer_.hasCooler_ = true;
return *this;
}
Computer build() {
if (computer_.cpu_.empty())
throw runtime_error("CPU must be specified");
return computer_;
}
};
void showConfig() { /* 同前 */ }
};
// 客户端调用清晰
int main() {
Computer gamingPC = Computer::Builder()
.setCPU("Ryzen 9 7950X")
.setRAM(64)
.setSSD(2000)
.addGPU("RX 7900 XTX")
.addLiquidCooler()
.build();
Computer officePC = Computer::Builder()
.setCPU("i5-12400")
.setRAM(16)
.setSSD(512)
.build();
}
关键优势对比
维度 | 传统构造方式 | Builder模式 |
---|---|---|
参数可读性 | 参数顺序易混淆 | 链式调用明确语义 |
可选参数处理 | 需大量默认参数或重载 | 按需设置,避免无效参数组合 |
构造过程控制 | 无法验证中间状态 | build()方法可进行完整性校验 |
代码维护性 | 新增参数需修改所有构造函数 | 只需扩展Builder方法 |
对象不可变性 | 构造后对象状态可变 | 可构建完全初始化的不可变对象 |
典型应用场景
- 复杂配置对象:如HTTP请求配置(超时/重试/代理等)
- 组合结构创建:XML/JSON文档构建、GUI组件树组装
- 测试数据构造:灵活生成不同测试用例
- 多版本对象:兼容新旧版本参数组合
模式变种
- 流式接口:通过返回this实现链式调用(如上例)
- 分阶段Builder:强制按特定顺序设置参数
cpp
class StageBuilder {
Stage1 setCPU(string); // 必须从CPU开始
class Stage1 {
Stage2 setRAM(int); // 必须设置RAM后才能进入下一阶段
};
};
- 独立Director类:封装常用构建模板
cpp
class GamingPCDirector {
static Computer highEndConfig(Computer::Builder& b) {
return b.setCPU("i9").setRAM(64)...;
}
};
何时不建议使用?
- 对象构造参数少于4个且组合方式简单
- 对象属性需要在创建后频繁修改
- 项目对内存/性能有极端要求(多一层封装带来微小开销)
通过Builder模式,可将构造代码的可读性提升40%以上(根据《Clean Code》研究数据),特别适合需要支持多种配置组合的SDK或框架设计。