目录
一、问题导入
我们点一个汉堡,商家按固定搭配做好,我们直接拿成品,这是工厂方法 ------ 没法选肉饼、配菜,只能接受预设方案。但如果我们自己一步一步选、一步一步组合:要牛肉饼、加生菜、抹番茄酱,最后拼成汉堡,这就是建造者模式。
从中能看出,工厂方法 是一步拿结果,建造者模式 是多步定制、最后得结果。接下来,我们进一步探讨建造者模式。
二、结构(如果只是应付考试的话,可以不细看)
接着从上述案例出发,我们首先需要定义一个产品类hambuger 。之后,提供一个抽象生成器hamburger_builder 用来生成汉堡。然后,还可以提供具体生成器 ,用以生成固定的产品,比如用以生成安格斯牛堡的beef_hambuger_builder,让他提供固定的流程。最后,只需要再去提供一个导演类director去指导builder来生成想要的产品。
那么,他的整体结构如下:

下面,给出了相应的示例代码:
在进行使用的过程中,我们可以通过director类来获取预定好的组成部分(个人认为该种形式无法很明显地体现建造者模式 和工厂模式的区别)
也可以通过直接使用CustomerBurgerBuilder来进行自定义汉堡内容
cpp
#pragma once
#include <iostream>
#include <string>
// 命名空间:与你的_AbstractFactory风格保持一致
namespace _BuilderPattern
{
// 1. 产品:汉堡(最终要构建的复杂对象)
class Burger
{
private:
std::string patty_; // 肉饼(成员变量用_结尾)
std::string vegetable_;// 蔬菜
std::string sauce_; // 酱料
public:
// 构造与析构(默认实现)
Burger() = default;
~Burger() = default;
// 设置汉堡各部分(接口函数)
void setPatty(const std::string& patty) { patty_ = patty; }
void setVegetable(const std::string& vegetable) { vegetable_ = vegetable; }
void setSauce(const std::string& sauce) { sauce_ = sauce; }
// 展示汉堡组成(功能函数)
void show() const
{
std::cout << "汉堡组成:" << patty_ << " + " << vegetable_ << " + " << sauce_ << std::endl;
}
};
// 2. 抽象建造者:定义汉堡构建的步骤接口
class BurgerBuilder
{
public:
BurgerBuilder() = default;
virtual ~BurgerBuilder() = default; // 虚析构,确保子类正确释放
// 纯虚函数:分步构建(参数化,支持自定义)
virtual void buildPatty(const std::string& patty) = 0;
virtual void buildVegetable(const std::string& vegetable) = 0;
virtual void buildSauce(const std::string& sauce) = 0;
// 获取最终产品
virtual Burger* getResult() = 0;
};
// 3. 具体建造者:实现汉堡的构建逻辑
class CustomBurgerBuilder : public BurgerBuilder
{
private:
Burger* burger_; // 持有当前构建的汉堡对象
public:
CustomBurgerBuilder()
{
burger_ = new Burger(); // 初始化空汉堡
}
~CustomBurgerBuilder() override
{
delete burger_; // 释放资源
}
// 实现抽象方法:设置具体配料
void buildPatty(const std::string& patty) override
{
burger_->setPatty(patty);
}
void buildVegetable(const std::string& vegetable) override
{
burger_->setVegetable(vegetable);
}
void buildSauce(const std::string& sauce) override
{
burger_->setSauce(sauce);
}
// 返回构建完成的汉堡
Burger* getResult() override
{
return burger_;
}
};
// 4. 导演:封装预设构建流程(可选,简化常用组合)
class Director
{
private:
BurgerBuilder* builder_; // 依赖抽象建造者(多态)
public:
// 传入具体建造者
Director(BurgerBuilder* builder) : builder_(builder) {}
~Director() = default;
// 预设方案1:经典牛肉汉堡
void buildClassicBeefBurger()
{
builder_->buildPatty("炭烤牛肉饼");
builder_->buildVegetable("生菜+番茄片");
builder_->buildSauce("番茄酱+芥末酱");
}
// 预设方案2:招牌鸡肉汉堡
void buildSignatureChickenBurger()
{
builder_->buildPatty("香煎鸡胸肉");
builder_->buildVegetable("黄瓜丝+洋葱圈");
builder_->buildSauce("蜂蜜沙拉酱");
}
};
// 测试函数:验证模式功能(与你的test()风格一致)
void test()
{
std::cout << "=== 建造者模式(汉堡案例)测试 ===" << std::endl;
// 场景1:用导演快速制作预设汉堡
CustomBurgerBuilder classicBuilder;
Director director(&classicBuilder);
director.buildClassicBeefBurger(); // 调用预设流程
Burger* classicBurger = classicBuilder.getResult();
std::cout << "【经典牛肉堡】" << std::endl;
classicBurger->show();
// 场景2:完全自定义汉堡(体现建造者的核心价值)
CustomBurgerBuilder myBuilder;
myBuilder.buildPatty("双层植物肉"); // 自定义肉饼
myBuilder.buildVegetable("牛油果+紫甘蓝"); // 自定义蔬菜
myBuilder.buildSauce("蒜香蛋黄酱"); // 自定义酱料
Burger* myBurger = myBuilder.getResult();
std::cout << "\n【自定义植物汉堡】" << std::endl;
myBurger->show();
}
}
三、优劣
优势:
(1)分离构建过程和表示形式,使构建过程更具灵活性,并允许构建不同的表示形式
(比如做汉堡时,"选配料的过程" 和 "最终汉堡的样子" 是分离的,既可以做经典牛肉堡,也可以自定义植物汉堡,灵活性很高。)
(2)可以更好地控制构建过程,并隐藏具体的构建细节
(用户只需要关心 "选什么配料",不需要知道汉堡是怎么组装的(比如面包怎么夹、酱料怎么涂),细节被隐藏了。)
(3)代码可复用性高,同一个构建器可在不同的构建流程中复用
(同一个 CustomBurgerBuilder
既可以给导演用来做预设汉堡,也可以自己用来做自定义汉堡,不用重复写逻辑。)
劣势:
(1)若产品的属性较少,建造者模式可能会导致代码冗余。
( 如果汉堡只有 "肉饼" 一个属性,用建造者模式就没必要,直接一个工厂方法更简单。)
(2)建造者模式会增加系统中类和对象的数量。
( 产品、抽象建造者、具体建造者、导演,类的数量比简单工厂多,小项目可能觉得 "重"。)
四、另一种形式
建造者模式还有另一种表达形式,那就是在构造函数当中提供所有的预设值,而在其每一个设置属性的函数当中,其设置返回值为当前对象自身(this),从而在进行对象的实例化的时候,通过链式调用来进行初始化。
不过,这种写法将会导致类的构造函数十分冗杂,我个人并不喜欢这种表达形式。
五、个人理解
建造者模式 实际上也是一种创建对象的方式,就工厂模式而言,我们可以获得一个固定的产品。但是,当产品的属性变得多样且不可控的时候,他们的整体制作流程往往不需要改变,那么,用户就只需要考虑单个组件如何进行选择即可。
就比如常见的换装游戏,我们自然可以预先设定好几套风格的衣服供玩家自行选择(古风还是现代风的)。但是,往往我们也需要"打开衣橱",让玩家自行一一挑选。不过,不管我们先选头发还是先选帽子,其渲染的结果始终是先进行头发的渲染,后进行帽子的渲染,从而确保帽子覆盖在头发上面,这便是固化的流程,其对于用户来说是看不到的。(这是我能够想到的一个比较典型且契合的应用。)