文章目录
代码仓库
建一栋房子总共分几步?建造者模式告诉你答案!
"把大象装冰箱,总共分几步?"
"三步。第一步,打开冰箱门;第二步,把大象装进冰箱;第三步,把冰箱门关上。"
Jungle活了这20多年,全靠这个笑话活着! 把大象装冰箱竟然只需要三步?那到底是怎么把大象装进冰箱呢?你问我,我问谁?再说,我也不关心这个呀!这......来点实际的吧,如果Jungle要建一栋房子,总共分几步?本文的建造者模式将声情并茂地向您娓娓道来......
建造者模式简介
建造者模式用于创建过程稳定,但配置多变的对象 。在《设计模式》一书中的定义是:将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。
建造者模式将客户端与包含多个部件的复杂对象的创建过程分离,客户端不必知道复杂对象的内部组成方式与装配方式(就好像Jungle不知道到底是如何把大象装进冰箱一样),只需知道所需建造者的类型即可。
建造者模式定义:
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
"同样的构建过程可以创建不同的表示"??这句话是什么意思呢?想象一下,建造一栋房子,建造过程无非都是打地基、筑墙、安装门窗等过程,但不同的客户可能希望不同的风格或者过程,最终建造出来的房子当然就呈现不同的风格啦!
经典的"建造者-指挥者"模式现在已经不太常用了,现在建造者模式主要用来通过链式调用生成不同的配置。比如我们要制作一杯珍珠奶茶。它的制作过程是稳定的,除了必须要知道奶茶的种类和规格外,是否加珍珠和是否加冰是可选的。
使用建造者模式的好处是不用担心忘了指定某个配置,保证了构建过程是稳定的。在 OkHttp、Retrofit 等著名框架的源码中都使用到了建造者模式。

建造者模式结构

建造者模式UML类图如下:

建造者模式代码实例
考虑这样一个场景,如下图:
Jungle想要建造一栋简易的房子(地板、墙和天花板),两个工程师带着各自的方案找上门来,直接给Jungle看方案和效果图。
犹豫再三,Jungle最终选定了一位工程师......交房之日,Jungle满意的看着建好的房子,
开始思考:这房子究竟是怎么建成的呢?这地板、墙和天花板是怎么建造的呢?
工程师笑着说:"It's none of your business"
UML图如下:
定义产品类House
House是本实例中的产品,具有floor、wall和roof三个属性。
cpp
class House {
public:
House() {}
void setFloor(string iFloor) {
this->floor = iFloor;
}
void setWall(string iWall) {
this->wall = iWall;
}
void setRoof(string iRoof) {
this->roof = iRoof;
}
// 打印House信息
void printfHouseInfo() {
// this->floor 是一个 std::string 类型,而C语言函数 printf 无法直接输出 std::string 类型。
// 使用 .c_str() 就能将 std::string 转换为一个 const char*(C风格字符串) ,供 printf 使用。
printf("Floor:%s\t\n", this->floor.c_str());
printf("Wall:%s\t\n", this->wall.c_str());
printf("Roof:%s\t\n", this->roof.c_str());
}
private:
string floor;
string wall;
string roof;
};
定义建造者
定义抽象建造者AbstractBuilder
cpp
// 抽象建造者AbstractBuilder
class AbstractBuilder {
public:
AbstractBuilder() : house(std::make_unique<House>()) {}
virtual ~AbstractBuilder() = default; // 使用默认析构函数即可
AbstractBuilder(const AbstractBuilder&) = delete;
AbstractBuilder& operator=(const AbstractBuilder&) = delete;
virtual void buildFloor() = 0;
virtual void buildWall() = 0;
virtual void buildRoof() = 0;
virtual std::unique_ptr<House> getHouse() {
return std::move(house); // 转移所有权
}
protected:
std::unique_ptr<House> house;
};
定义具体建造者
cpp
// 具体建造者ConcreteBuilderA
// 子类无需再定义getHouse()和析构函数,因为基类已经完成这些任务。
class ConcreteBuilderA: public AbstractBuilder {
public:
ConcreteBuilderA() { printf("ConcreteBuilderA\n"); }
void buildFloor() override { house->setFloor("Floor_A"); }
void buildWall() override { house->setWall("Wall_A"); }
void buildRoof() override { house->setRoof("Roof_A"); }
};
// 具体建造者ConcreteBuilderB
class ConcreteBuilderB: public AbstractBuilder {
public:
ConcreteBuilderB() { printf("ConcreteBuilderB\n"); }
void buildFloor() override { house->setFloor("Floor_B"); }
void buildWall() override { house->setWall("Wall_B"); }
void buildRoof() override { house->setRoof("Roof_B"); }
};
定义指挥者
cpp
// 指挥者Director
class Director {
public:
Director(): builder(nullptr) {}
// Builder的生命周期应由调用方自己管理,不能由Director删除,否则可能造成未知问题。
~Director() = default;
Director(const Director&) = delete;
Director& operator=(const Director&) = delete;
void setBuilder(AbstractBuilder* iBuilder) {
this->builder = iBuilder;
}
std::unique_ptr<House> construct() {
builder->buildFloor();
builder->buildWall();
builder->buildRoof();
return builder->getHouse(); // 返回智能指针
}
private:
AbstractBuilder* builder;
};
客户端代码示例
cpp
#include "BuilderPattern.h"
int main() {
// 抽象建造者
AbstractBuilder* builder = nullptr;
// 指挥者 (生命周期在main中)
Director director;
// 指定具体建造者A
builder = new ConcreteBuilderA();
director.setBuilder(builder);
// house的所有权转移给调用方
std::unique_ptr<House> houseA = director.construct();
houseA->printfHouseInfo();
// 注意:house内存已被外部接管,不应由builder释放
delete builder; // 此时builder的析构函数应避免删除house
// delete houseA; // 由调用方手动释放house内存
// 指定具体建造者B (这里应改成ConcreteBuilderB)
builder = new ConcreteBuilderB();
director.setBuilder(builder);
std::unique_ptr<House> houseB = director.construct();
houseB->printfHouseInfo();
delete builder; // 同上
// delete houseB; // 调用方负责释放内存
return 0;
}
运行结果

建造者模式总结
从客户端代码可以看到,客户端只需指定具体建造者,并作为参数传递给指挥者,通过指挥者即可得到结果。**客户端无需关心House的建造方法和具体流程。如果要更换建造风格,只需更换具体建造者即可,不同建造者之间并无任何关联,方便替换。**从代码优化角度来看,其实可以不需要指挥者Director的角色,而直接把construct方法放入具体建造者当中。

之后我会持续更新,如果喜欢我的文章,请记得一键三连哦,点赞关注收藏,你的每一个赞每一份关注每一次收藏都将是我前进路上的无限动力 !!!↖(▔▽▔)↗感谢支持!