深入浅出设计模式——创建型模式之建造者模式 Builder

发布于:2025-08-04 ⋅ 阅读:(9) ⋅ 点赞:(0)


代码仓库
建一栋房子总共分几步?建造者模式告诉你答案!
“把大象装冰箱,总共分几步?”
“三步。第一步,打开冰箱门;第二步,把大象装进冰箱;第三步,把冰箱门关上。”

Jungle活了这20多年,全靠这个笑话活着! 把大象装冰箱竟然只需要三步?那到底是怎么把大象装进冰箱呢?你问我,我问谁?再说,我也不关心这个呀!这……来点实际的吧,如果Jungle要建一栋房子,总共分几步?本文的建造者模式将声情并茂地向您娓娓道来……

建造者模式简介

建造者模式用于创建过程稳定,但配置多变的对象。在《设计模式》一书中的定义是:将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。

建造者模式将客户端与包含多个部件的复杂对象的创建过程分离,客户端不必知道复杂对象的内部组成方式与装配方式(就好像Jungle不知道到底是如何把大象装进冰箱一样),只需知道所需建造者的类型即可。

建造者模式定义:
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

“同样的构建过程可以创建不同的表示”??这句话是什么意思呢?想象一下,建造一栋房子,建造过程无非都是打地基、筑墙、安装门窗等过程,但不同的客户可能希望不同的风格或者过程,最终建造出来的房子当然就呈现不同的风格啦!

经典的“建造者-指挥者”模式现在已经不太常用了,现在建造者模式主要用来通过链式调用生成不同的配置。比如我们要制作一杯珍珠奶茶。它的制作过程是稳定的,除了必须要知道奶茶的种类和规格外,是否加珍珠和是否加冰是可选的。

使用建造者模式的好处是不用担心忘了指定某个配置,保证了构建过程是稳定的。在 OkHttp、Retrofit 等著名框架的源码中都使用到了建造者模式。

在这里插入图片描述

建造者模式结构

在这里插入图片描述
建造者模式UML类图如下:

在这里插入图片描述

建造者模式代码实例

考虑这样一个场景,如下图:
在这里插入图片描述
Jungle想要建造一栋简易的房子(地板、墙和天花板),两个工程师带着各自的方案找上门来,直接给Jungle看方案和效果图。
犹豫再三,Jungle最终选定了一位工程师……交房之日,Jungle满意的看着建好的房子,
开始思考:这房子究竟是怎么建成的呢?这地板、墙和天花板是怎么建造的呢?
工程师笑着说:“It’s none of your business”

UML图如下:
在这里插入图片描述

定义产品类House

House是本实例中的产品,具有floor、wall和roof三个属性。

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

// 抽象建造者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;
};

定义具体建造者

// 具体建造者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"); }
};

定义指挥者

// 指挥者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;
};

客户端代码示例

#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方法放入具体建造者当中。

在这里插入图片描述

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


网站公告

今日签到

点亮在社区的每一天
去签到