目录
一.专栏简介
本专栏是我学习《head first》设计模式的笔记。这本书中是用Java语言为基础的,我将用C++语言重写一遍,并且详细讲述其中的设计模式,涉及是什么,为什么,怎么做,自己的心得等等。希望阅读者在读完我的这个专题后,也能在开发中灵活且正确的使用,或者在面对面试官时,能够自信地说自己熟悉常用设计模式。
本章将开始生成器模式的学习,也叫建造者模式(builder pattern)。
二.生成器模式概念
使用生成器模式来封装一个产品的构造过程,并允许按步骤构造。
也就是一个产品可以由很多组件组成,我们可以将组件的构造封装起来 ,并且控制组件构造的先后顺序。
下面通过组装电脑的例子来说明。
三.组装电脑案例和代码
电脑的组装是一个很讲究的事,比如我们一般需要先安装CPU,因为涉及主板的安装,然后安装GPU,避免空间冲突,毕竟GPU体积较大,最后安装RAM。
此时我们就可以使用建造者模式来控制组件的构造和组件的安装顺序,这里的组件就是一个一个的硬件。
建造者模式分为四种类,首先是产品(Product)类。这里的Product就是电脑,代码如下:
cpp
// 1.Product:电脑(复杂对象)
class Computer
{
public:
void setCpu(const string& cpu) { _cpu = cpu; }
void setGpu(const string& gpu) { _gpu = gpu; }
void setRam(const string& ram) { _ram = ram; }
void showInfo()
{
cout << "当前电脑配置:cpu -> " << _cpu << " gpu -> " << _gpu << " ram -> " << _ram << endl;;
}
private:
string _cpu;
string _gpu;
string _ram;
};
这个Computer类里面就是设置组件(硬件)的函数 和打印函数,这里我们直接使用string来表示各个硬件。
其次是抽象构建接口(Builder),也就是构建者这个抽象类。代码如下:
cpp
// 2.Builder:抽象构建接口
class ComputerBuilder
{
public:
ComputerBuilder(Computer* computer): _computer(computer)
{}
virtual ~ComputerBuilder() = default;
virtual void buildCpu() = 0;
virtual void buildGpu() = 0;
virtual void buildRam() = 0;
virtual Computer* getResult() = 0;
protected:
Computer* _computer;
};
ComputerBuilder类中涉及各个组件(硬件)的构造和获取Computer对象的接口。这里有一个需要注意的点,就是基类的虚函数需要声明为虚函数,否则会造成内存泄漏。
下面我们看抽象构建接口(Builder) 的子类具体构建者(ConcreteBuilder),我们这里构建游戏本和办公本,所以有两个具体构建者。代码如下:
cpp
// 3.ConcreteBuilder:具体构建者(游戏本)
class GamingComputerBuilder : public ComputerBuilder
{
public:
GamingComputerBuilder(): ComputerBuilder(new Computer())
{}
void buildCpu() override
{
_computer->setCpu("9800x3d");
}
void buildGpu() override
{
_computer->setGpu("RTX5090");
}
void buildRam() override
{
_computer->setRam("64GB DDR5");
}
Computer* getResult() { return _computer; }
};
// 3.ConcreteBuilder:具体构建者(办公本)
class OfficeComputerBuilder : public ComputerBuilder
{
public:
OfficeComputerBuilder() : ComputerBuilder(new Computer())
{}
void buildCpu() override
{
_computer->setCpu("i5-13400");
}
void buildGpu() override
{
_computer->setGpu("集成显卡");
}
void buildRam() override
{
_computer->setRam("16GB DDR4");
}
Computer* getResult() { return _computer; }
};
这里具体构建者里在构造函数里构造父类对象并且重写父类的纯虚函数,游戏本设置的硬件配置就非常高,办公本则是实用主义。
最后就是我们的重头戏:指挥者(Director),负责调用具体构建者构造各个组件并且控制组装顺序。代码如下:
cpp
// 4. Director:指挥者(负责组装顺序)
class Director
{
public:
void setBuilder(ComputerBuilder* computerBuilder)
{
_computerBuilder = computerBuilder;
}
Computer* construct()
{
if (_computerBuilder == nullptr) return nullptr;
_computerBuilder->buildCpu(); // 先装CPU,涉及主板安装
_computerBuilder->buildGpu(); // 再装GPU,避免空间冲突
_computerBuilder->buildRam(); // 最后装RAM
return _computerBuilder->getResult();
}
private:
ComputerBuilder* _computerBuilder = nullptr;
};
指挥者(Director) 首先设置具体构建者 的抽象构建者指针或者引用 ,达到多态的目的。construct()函数里构造各个组件 (硬件),控制组件构造的先后顺序,并且通过构建者返回这个构造好的Computer对象的指针。
客户端主要围绕**指挥者(Director)**来组装电脑,我们组装一个游戏本和一个办公本,代码如下:
cpp
#include "builder.h"
int main()
{
Director director;
Computer* computer = nullptr;
ComputerBuilder* builder = nullptr;
builder = new GamingComputerBuilder();
director.setBuilder(builder);
computer = director.construct();
cout << "游戏本:";
computer->showInfo();
delete builder;
delete computer;
builder = new OfficeComputerBuilder();
director.setBuilder(builder);
computer = director.construct();
cout << "办公本:";
computer->showInfo();
delete builder;
delete computer;
return 0;
}
运行结果如下:

到此我们的建造者就演示完毕。
四.生成器的优点
- 封装复杂对象的构造方式。
- 允许对象用多个步骤构造,并且可以改变步骤顺序(和一步到位的工厂不同)。
- 向客户隐藏产品的内部表现,我们的main函数对Computer的内部构造一无所知。
- 产品的实现可以被替换,得益于我们的具体构建者继承抽象构建接口,并且指挥者组合了这个抽象构建接口的指针,这里就是还使用了策略模式。
五.生成器的用途和缺点
- 用途:经常被用来建造组合结构,我们的例子中一台电脑组合了CPU ,GPU 和RAM。
- 缺点:和使用工厂相比,构造对象需要更多客户的领域知识。在我们例子中,也就是一个建造者(Builder)需要提供三个接口去分别建造CPU,GPU和RAM,一个指挥者(Director)需要调用这3个接口。