C++ 建造者模式(Builder Pattern)
目录
- 模式定义与核心思想
- 适用场景 & 解决的痛点
- 四大核心角色 & UML结构
- 经典标准实现(抽象建造者+指挥者)
- C++常用三种简化实现
- 流式链式建造者(无抽象)
- 静态内部类建造者(Java风格C++适配)
- 不可变对象建造者
- 建造者 vs 工厂 vs 原型模式对比
- 优缺点深度分析
- 工程开发最佳实践规范
- 常见坑点与避坑指南
- 面试常考题+标准答案
- C++标准库/开源框架实际应用
1. 模式定义与核心思想
定义
建造者模式属于创建型设计模式,将复杂对象的构建过程与对象的内部表示分离开,使得同样的构建过程可以创建不同的对象实例。
核心思想
- 拆分:把构造步骤和最终产品解耦
- 分步:零件化、链式一步步配置属性
- 复用:同一套构建流程,造出不同配置产品
- 收口:统一
build()方法生成对象,集中参数校验
核心价值
解决多参数构造函数重叠、参数顺序混乱、重载爆炸、无默认值、无法参数校验五大问题。
2. 适用场景
- 对象具有大量可选参数(≥5个),参数组合繁杂。
- 需要分步创建对象,不同步骤生成不同配置。
- 需要严格控制对象创建过程,创建后不可修改(不可变对象)。
- 希望屏蔽对象内部复杂组装细节,使用者只关心配置。
- 同一构建流程,需要生成多种不同形态的产品。
- 替代重叠构造函数、重叠
setter泛滥的糟糕写法。
不适用场景
- 属性极少、结构简单的对象(过度设计)。
- 对象几乎无可选配置,固定结构。
3. 四大核心角色 & UML说明
| 角色 | 职责说明 |
|---|---|
| Product 产品 | 最终要生成的复杂对象,包含多个成员属性 |
| Builder 抽象建造者 | 抽象接口,定义构建各个部件的纯虚方法 + build接口 |
| ConcreteBuilder 具体建造者 | 实现抽象建造者,维护中间属性,完成部件组装 |
| Director 指挥者 | 调用建造者接口,编排构建步骤,封装固定创建流程 |
关键:Director 可选,实际开发中90%场景可省略,客户端直接调用建造者链式配置。
4. 经典标准实现(抽象建造者 + 指挥者)
4.1 产品类
#include<iostream>#include<string>#include<map>#include<memory>// 产品:电脑复杂对象classComputer{public:std::string cpu;std::string gpu;std::string memory;std::string disk;intpower;voidshowInfo()const{std::cout<<"===== 电脑配置 =====\n";std::cout<<"CPU: "<<cpu<<"\nGPU: "<<gpu<<"\n内存: "<<memory<<"\n硬盘: "<<disk<<"\n电源: "<<power<<"W\n";}};4.2 抽象建造者
// 抽象建造者classComputerBuilder{public:virtual~ComputerBuilder()=default;virtualvoidbuildCpu(conststd::string&cpu)=0;virtualvoidbuildGpu(conststd::string&gpu)=0;virtualvoidbuildMemory(conststd::string&mem)=0;virtualvoidbuildDisk(conststd::string&disk)=0;virtualvoidbuildPower(intwatt)=0;virtualstd::unique_ptr<Computer>build()=0;};4.3 具体建造者
// 游戏电脑建造者classGameComputerBuilder:publicComputerBuilder{private:Computer comp;public:voidbuildCpu(conststd::string&cpu)override{comp.cpu=cpu;}voidbuildGpu(conststd::string&gpu)override{comp.gpu=gpu;}voidbuildMemory(conststd::string&mem)override{comp.memory=mem;}voidbuildDisk(conststd::string&disk)override{comp.disk=disk;}voidbuildPower(intwatt)override{comp.power=watt;}std::unique_ptr<Computer>build()override{returnstd::make_unique<Computer>(comp);}};// 办公电脑建造者classOfficeComputerBuilder:publicComputerBuilder{private:Computer comp;public:voidbuildCpu(conststd::string&cpu)override{comp.cpu=cpu;}voidbuildGpu(conststd::string&/*gpu*/)override{comp.gpu="核显";}voidbuildMemory(conststd::string&mem)override{comp.memory=mem;}voidbuildDisk(conststd::string&disk)override{comp.disk=disk;}voidbuildPower(intwatt)override{comp.power=watt;}std::unique_ptr<Computer>build()override{returnstd::make_unique<Computer>(comp);}};4.4 指挥者
// 指挥者:固定组装流程classDirector{public:voidconstructGamePC(ComputerBuilder&builder){builder.buildCpu("Intel i9");builder.buildGpu("RTX 4090");builder.buildMemory("32G DDR5");builder.buildDisk("2T SSD");builder.buildPower("850");}voidconstructOfficePC(ComputerBuilder&builder){builder.buildCpu("Intel i5");builder.buildMemory("16G DDR4");builder.buildDisk("512G SSD");builder.buildPower("400");}};4.5 调用示例
intmain(){Director director;GameComputerBuilder gameBuilder;director.constructGamePC(gameBuilder);autogamePc=gameBuilder.build();gamePc->showInfo();OfficeComputerBuilder officeBuilder;director.constructOfficePC(officeBuilder);autoofficePc=officeBuilder.build();officePc->showInfo();return0;}5. C++三种工程常用简化实现
5.1 简化版一:流式链式建造者(最常用)
去掉抽象类、去掉指挥者,方法返回引用支持链式调用,工业级首选。
// 产品classPerson{friendclassPersonBuilder;private:std::string name;intage{0};std::string addr;// 私有构造,禁止外部直接创建Person()=default;public:voidprint()const{std::cout<<name<<", "<<age<<"岁, 地址:"<<addr<<"\n";}};// 链式建造者classPersonBuilder{private:Person p;public:PersonBuilder&name(conststd::string&n){p.name=n;return*this;}PersonBuilder&age(inta){p.age=a;return*this;}PersonBuilder&addr(conststd::string&ad){p.addr=ad;return*this;}// 统一校验 + 构建Personbuild(){if(p.name.empty())throwstd::runtime_error("姓名不能为空");if(p.age<0)p.age=0;returnp;}};// 使用intmain(){Person p=PersonBuilder().name("张三").age(28).addr("上海").build();p.print();return0;}5.2 简化版二:静态内部类建造者(模仿Java写法)
适合配置类、参数实体,可读性极强。
classConfig{public:std::string ip;intport;inttimeout;// 静态内部建造者staticclassBuilder{private:Config cfg;public:Builder&ip(conststd::string&host){cfg.ip=host;return*this;}Builder&port(intp){cfg.port=p;return*this;}Builder&timeout(intt){cfg.timeout=t;return*this;}Configbuild(){returncfg;}};};// 调用// Config cfg = Config::Builder().ip("127.0.0.1").port(8080).build();5.3 简化版三:不可变对象建造者
创建后属性不可修改,线程安全、适合常量配置、协议报文。
关键点:成员const、无setter、仅建造者可初始化。
classImmutableMsg{friendclassMsgBuilder;private:conststd::string type;constintcode;conststd::string data;// 私有构造ImmutableMsg(std::string t,intc,std::string d):type(std::move(t)),code(c),data(std::move(d)){}public:voidshow()const{std::cout<<type<<" | "<<code<<" | "<<data<<"\n";}};classMsgBuilder{private:std::string type;intcode{0};std::string data;public:MsgBuilder&type(std::string t){type=std::move(t);return*this;}MsgBuilder&code(intc){code=c;return*this;}MsgBuilder&data(std::string d){data=std::move(d);return*this;}ImmutableMsgbuild(){returnImmutableMsg(type,code,data);}};6. 建造者 vs 工厂 vs 原型模式对比
| 维度 | 建造者模式 | 工厂方法/抽象工厂 | 原型模式 |
|---|---|---|---|
| 核心关注点 | 分步组装复杂对象 | 按类型创建产品 | 克隆已有对象 |
| 对象复杂度 | 高、多属性、多部件 | 结构固定、类型区分 | 任意对象,侧重拷贝 |
| 参数配置 | 支持自由组合、链式配置 | 创建时一次性传入参数 | 无需配置,直接克隆 |
| 适用场景 | 配置繁杂、分步构建 | 产品线固定、按工厂生产 | 对象创建开销大 |
| 扩展方式 | 新增建造者 | 新增工厂子类 | 实现克隆接口 |
选型口诀
- 多参数、可自由配置 → 建造者
- 按类型生产不同产品 → 工厂
- 初始化耗时大、频繁创建相似对象 → 原型
7. 优缺点深度分析
优点
- 隔离复杂创建逻辑:客户端无需关心对象内部组装细节。
- 链式调用可读性极强:告别冗长构造函数、参数顺序记忆。
- 支持默认值 + 统一参数校验:在
build()收口,保证对象合法。 - 开闭原则:新增产品配置只需新增建造者,不改动原有代码。
- 可实现不可变对象:创建后禁止修改,线程安全。
- 拆分构建与表示:同一流程生成不同产品。
缺点
- 增加类数量:每个产品配套一个建造者,代码量上升。
- 过度设计:简单对象使用反而冗余。
- 若产品内部结构变化,建造者也需同步修改,耦合略有增加。
8. C++工程开发最佳实践规范
- 业务开发优先用「流式链式建造者」,舍弃抽象建造者+指挥者,避免过度设计。
- 产品类构造函数私有,只允许建造者
friend访问,防止随意构造。 - 所有配置接口返回
*this,强制支持链式调用。 - 参数校验统一放在 build(),不分散在每个set方法。
- 给可选参数设置合理默认值,减少必传参数。
- 配置类、网络请求体、协议报文强制使用建造者替代多参数构造。
- 不需要Director就不写,客户端直接链式配置更灵活。
- 高频固定配置可封装快捷静态方法,如
PersonBuilder::createDefault()。
9. 常见坑点与避坑指南
- 坑:建造者成员和产品成员不同步,漏赋值。
避坑:建造者完整复刻产品所有属性,一一对应。 - 坑:未私有产品构造,外部仍可随意new。
避坑:构造私有,友元授权建造者。 - 坑:链式方法忘记返回
*this,断链无法连续调用。
避坑:严格规范每个配置方法都返回建造者引用。 - 坑:参数校验分散在各个set函数,逻辑混乱。
避坑:全部校验收拢到build()。 - 坑:简单对象强行用建造者,代码臃肿。
避坑:属性≤3个直接构造函数即可。
10. 面试常考题 + 标准答案
Q1:建造者模式和工厂模式区别?
答:建造者侧重分步组装复杂对象、自由配置属性;工厂模式侧重按类型生产固定结构产品。建造者适合多参数可变配置,工厂适合产品线分类创建。
Q2:为什么要用建造者模式,不用重载构造函数?
答:重载构造函数会出现参数爆炸、顺序易错、无法跳过可选参数、不能统一校验;建造者链式调用语义清晰、按需配置、收口校验、可读性高。
Q3:Director指挥者必须要有吗?
答:非必须。标准范式有,实际工程90%场景省略,客户端直接链式配置更灵活,指挥者只在需要固定统一构建流程时使用。
Q4:C++如何实现不可变对象建造者?
答:产品成员设为const、私有构造、无外部set接口,仅由建造者一次性构造初始化,创建后无法修改。
11. C++实际框架应用场景
- 网络库:HTTP请求、TCP连接参数、URL 构建器。
- 配置中心:服务配置、数据库配置、日志配置。
- 协议报文:protobuf/自定义报文组装。
- GUI框架:窗口、控件属性复杂构造。
- 数据库ORM:查询条件链式构造。