基于C++的《Head First设计模式》笔记——生成器模式

目录

一.专栏简介

二.生成器模式概念

三.组装电脑案例和代码

四.生成器的优点

五.生成器的用途和缺点


一.专栏简介

本专栏是我学习《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的内部构造一无所知。
  • 产品的实现可以被替换,得益于我们的具体构建者继承抽象构建接口,并且指挥者组合了这个抽象构建接口的指针,这里就是还使用了策略模式。

五.生成器的用途和缺点

  • 用途:经常被用来建造组合结构,我们的例子中一台电脑组合了CPUGPURAM
  • 缺点:和使用工厂相比,构造对象需要更多客户的领域知识。在我们例子中,也就是一个建造者(Builder)需要提供三个接口去分别建造CPU,GPU和RAM,一个指挥者(Director)需要调用这3个接口。
相关推荐
一定要AK1 小时前
Spring 入门核心笔记
java·笔记·spring
研究点啥好呢1 小时前
Github热门项目推荐 | 创建你的像素风格!
c++·python·node.js·github·开源软件
_dindong1 小时前
cf1091div2 C.Grid Covering(数论)
c++·算法
沫璃染墨2 小时前
C++ string 从入门到精通:构造、迭代器、容量接口全解析
c语言·开发语言·c++
6Hzlia2 小时前
【Hot 100 刷题计划】 LeetCode 17. 电话号码的字母组合 | C++ 回溯算法经典模板
c++·算法·leetcode
_李小白2 小时前
【OSG学习笔记】Day 38: TextureVisitor(纹理访问器)
android·笔记·学习
计算机安禾3 小时前
【数据结构与算法】第36篇:排序大总结:稳定性、时间复杂度与适用场景
c语言·数据结构·c++·算法·链表·线性回归·visual studio
unicrom_深圳市由你创科技3 小时前
做虚拟示波器这种实时波形显示的上位机,用什么语言?
c++·python·c#
无限进步_3 小时前
【C++】电话号码的字母组合:从有限处理到通用解法
开发语言·c++·ide·windows·git·github·visual studio
C++ 老炮儿的技术栈3 小时前
GCC编译时无法向/tmp 目录写入临时汇编文件,因为设备空间不足,解决
linux·运维·开发语言·汇编·c++·git·qt