C++设计模式之构造器
动机
在软件系统中,有时候面临着“一个复杂对象”的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。
如何应对这种变化?如何提供一种“封装机制”来隔离出“复杂对象的各个部分”的变化,从而保持系统中的“稳定构建算法”不随着需求改变而改变?
代码示例:建立各种房子,比如砖瓦房,高楼,别墅等房子
class House{//....
};class HouseBuilder {
public:House* GetResult(){return pHouse;}virtual ~HouseBuilder(){}
protected:House* pHouse;virtual void BuildPart1()=0;virtual void BuildPart2()=0;virtual void BuildPart3()=0;virtual void BuildPart4()=0;virtual void BuildPart5()=0;};class StoneHouse: public House{};// 石头房子的创建,override HouseBuilder里面的流程
class StoneHouseBuilder: public HouseBuilder{
protected:virtual void BuildPart1(){//pHouse->Part1 = ...;}virtual void BuildPart2(){}virtual void BuildPart3(){}virtual void BuildPart4(){}virtual void BuildPart5(){}};// 房子具体构建过程
class HouseDirector{public:// 有一个HouseBuilder的指针HouseBuilder* pHouseBuilder;HouseDirector(HouseBuilder* pHouseBuilder){this->pHouseBuilder=pHouseBuilder;}House* Construct(){ // 整个构建流程是稳定的pHouseBuilder->BuildPart1();for (int i = 0; i < 4; i++){pHouseBuilder->BuildPart2();}bool flag=pHouseBuilder->BuildPart3();if(flag){pHouseBuilder->BuildPart4();}pHouseBuilder->BuildPart5();return pHouseBuilder->GetResult();}
};int main() {// 创建石头房子的建造者StoneHouseBuilder stoneHouseBuilder;// 创建房子Director,并指定建造者HouseDirector houseDirector(&stoneHouseBuilder);// 构建房子House* stoneHouse = houseDirector.Construct();// 输出结果if (stoneHouse != nullptr) {// 假设 House 类有适当的输出方法// 这里只是简单示例,实际使用时需要根据具体类的实现调用相应的方法std::cout << "Stone House constructed." << std::endl;} else {std::cout << "Failed to construct Stone House." << std::endl;}// 释放资源delete stoneHouse;return 0;
}
上述代码涉及了Builder设计模式,包括以下几个类:
House
类:
表示待构建的产品,即房子。在示例中,StoneHouse
类是 House
的具体实现。
HouseBuilder
抽象类:
定义了构建产品(房子)的抽象接口。具体的建造者(例如 StoneHouseBuilder
)需要继承这个抽象类并实现接口中的方法,以完成具体产品的构建。
StoneHouseBuilder
类:
是HouseBuilder
的具体实现,负责构建石头房子。它通过实现抽象接口中的方法来完成具体的建造流程。
HouseDirector
类:
起到指导建造的作用,通过构造函数接收一个具体的建造者对象。通过调用建造者的方法,按照一定的构建流程组织建造者完成产品的构建。
main
函数:
在 main
函数中,创建了 StoneHouseBuilder 的实例,然后创建了一个HouseDirector
的实例,并将建造者传递给导演。通过导演的 Construct 方法
,按照一定的构建流程构建了石头房子,并输出结果。
总体而言,Builder
设计模式的目的是将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。在这个示例中,HouseBuilder
抽象类定义了构建房子的接口,StoneHouseBuilder
实现了具体的石头房子构建流程,而 HouseDirector
则负责协调建造者完成整个构建流程。
Builder要点总结
Builder 模式主要用于“分步骤构建一个复杂的对象”。在这其中“分步骤”是一个稳定的算法,而复杂对象的各个部分则经常变化。
变化点在哪里,封装哪里—— Builder模式主要在于应对“复杂对象各个部分”的频繁需求变动。其缺点在于难以应对“分步骤构建算法”的需求变动。
在Builder模式中,要注意不同语言中构造器内调用虚函数的差别(C++ vs. C#) 。
chatGPT给出的builder的例子
以下是一个简单的C++示例代码,演示Builder设计模式:
#include <iostream>
#include <string>// 产品类
class Product {
public:void AddPart(const std::string& part) {parts += part + " ";}void Show() const {std::cout << "Product Parts: " << parts << std::endl;}private:std::string parts;
};// 抽象建造者类
class Builder {
public:virtual void BuildPart1() = 0;virtual void BuildPart2() = 0;virtual Product* GetResult() = 0;
};// 具体建造者类
class ConcreteBuilder : public Builder {
public:ConcreteBuilder() {product = new Product();}void BuildPart1() override {product->AddPart("Part1");}void BuildPart2() override {product->AddPart("Part2");}Product* GetResult() override {return product;}private:Product* product;
};// 指导者类
class Director {
public:Director(Builder* builder) : builder(builder) {}void Construct() {builder->BuildPart1();builder->BuildPart2();}private:Builder* builder;
};int main() {// 创建具体建造者ConcreteBuilder concreteBuilder;// 创建指导者,并传入具体建造者Director director(&concreteBuilder);// 指导者构建产品director.Construct();// 获取最终产品Product* product = concreteBuilder.GetResult();// 展示产品if (product != nullptr) {product->Show();delete product;}return 0;
}
在这个示例中,Product
表示最终构建的复杂对象,Builder
是抽象建造者类,ConcreteBuilder
是具体建造者类,Director 是指导者类。通过指导者调用具体建造者的方法,最终得到构建完成的产品。这个例子中的产品是简单的包含两个部分的字符串,实际应用中可以根据需求定义更复杂的产品结构。
相关文章:
C++设计模式之构造器
动机 在软件系统中,有时候面临着“一个复杂对象”的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。 如何…...

红日靶场-5
环境搭建 这个靶场相对于前几个靶场来说较为简单,只有两台靶机,其中一台主机是win7,作为我们的DMZ区域的入口机,另外一台是windows2008,作为我们的域控主机,所以我们只需要给我们的win7配置两张网卡&#…...

做异端中的异端 -- Emacs裸奔之路3: 上古神键Hyper
谈一下快捷捷冲突的问题。 Emacs几乎穷尽所有组合键 我用下面命令,在Fundamental模式下,枚举所有绑定。 (defun keymap-lookup-test-fn(); printable keys(setq printable-chars (number-sequence 33 126))(setq i 0)(while (< i (length printable…...

Java多线程介绍及使用指南
“多线程”:并发 要介绍线程,首先要区分开程序、进程和线程这三者的区别。 程序:具有一定功能的代码的集合,但是是静态的,没有启动运行 进程:启动运行的程序【资源的分配单位】 线程:进程中的…...

HarmonyOS 5.0应用开发——列表(List)
【高心星出品】 文章目录 列表(List)列表介绍列表布局设置主轴方向设置交叉轴方向 列表填充分组列表填充 滚动条位置设置滚动位置滚到监听 列表项侧滑 列表(List) 列表介绍 列表作为一种容器,会自动按其滚动方向排列…...
自动化电气行业的优势和劣势是什么
优势 市场需求广泛: 自动化电气技术广泛应用于电力系统、制造业、交通、农业等多个领域,随着智能化、数字化趋势的加强,其市场需求持续增长。在智能制造、智能电网等领域,自动化电气技术更是发挥着关键作用,推动了行业…...
第 42 章 - Go语言 设计模式
在Go语言中,设计模式是一种被广泛接受的解决常见问题的最佳实践。这些模式可以分为三类:创建型模式、结构型模式和行为型模式。下面我将结合案例以及源代码对这三种类型的设计模式进行详细讲解。 创建型模式 创建型模式主要关注对象的创建过程…...
【机器学习】---大语言模型
引言:开启大语言模型的奇幻旅程 近年来,人工智能(AI)领域正在经历一场前所未有的技术革命,而其中最耀眼的明星莫过于大语言模型(Large Language Models, LLMs)。这些模型,犹如现代科…...

挑战用React封装100个组件【002】
项目地址 https://github.com/hismeyy/react-component-100 组件描述 组件适用于需要展示图文信息的场景,比如产品介绍、用户卡片或任何带有标题、描述和可选图片的内容展示 样式展示 代码展示 InfoCard.tsx import ./InfoCard.cssinterface InfoCardProps {t…...

MarkDown-插入图片-图片url地址的生成获取方法
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、url地址是什么二、如何获取图片的url地址1.了解图床2.使用图床获取图片URL地址2.1进入网站后,点击右下角“Select Image.”按钮,即可…...

插值、拟合和回归分析的相关知识
目录 0 序言 1 分段线性插值 2 多项式插值 3 样条插值 4 最小二乘拟合 5 多元线性回归 0 序言 在生产实践和科学研究中,常常有这些问题: 插值问题:由实验或测量得到变量间的一批离散样点,要求得到变量之间的函数关系或得到样点之外的…...

【小白学机器学习42】进行多次抽样,样本的分布参数和总体的分布参数的关系
目录 1 进行多次抽样,样本的分布参数和总体的分布参数的关系 2 样本容量越大,多次抽样的样本的分布参数和总体的分布参数的关系 3 随着样本容量增大,多次抽样均值的 平均值,方差的变化 4 随着样本容量增大,多次抽…...
链动星海 质引未来|中信银行加码科技金融 “接力式”服务助力“新质生产力”释放
11月26日,第二届中国国际供应链促进博览会(以下简称链博会)在北京中国国际展览中心开幕。中信集团以“链动星海 质引未来”为主题,亮相先进制造链展区。此次布展由中信金控主办、中信银行承办,携手中信证券、中信建投证…...
黑马2024AI+JavaWeb开发入门Day02-JS-VUE飞书作业
视频地址:哔哩哔哩 讲义作业飞书地址:飞书 一、作业1 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge">&l…...

云计算基础-期末复习
第一章:云计算概论 一、云计算的定义与特征 1. 定义: 云计算是一种通过网络以按需、可扩展的方式获取计算资源和服务的模式。它将计算资源视为一种公用事业,用户可以根据需求动态获取和释放资源,而无需了解底层基础设施的细节。…...

Java GET请求 请求参数在Body中使用Json格式传参
业务需要调个三方接口 使用GET请求方式 但是!请求参数不在Query中,竟然在Body中,使用Json格式传参 在API调试工具里面可以调通 在java代码里,死活调不通 网上搜了搜,找到一个靠谱的,记录一下 import o…...

AI数据分析工具(一)
Looker Studio(谷歌)-免费 优点 免费使用:对于中小型企业和个人用户来说,没有任何费用压力,可以免费享受到数据可视化和报表创建的功能。与Google服务集成:特别适合使用Google产品生态的企业,…...

go结构体匿名“继承“方法冲突时继承优先顺序
在 Go 语言中,匿名字段(也称为嵌入字段)可以用来实现继承的效果。当你在一个结构体中匿名嵌入另一个结构体时,嵌入结构体的方法会被提升到外部结构体中。这意味着你可以直接通过外部结构体调用嵌入结构体的方法。 如果多个嵌入结…...
【049】基于51单片机语音录放【Proteus仿真+Keil程序+报告+原理图】
☆、设计硬件组成:51单片机最小系统ISD4004语音芯片LM386音频放大器喇叭LCD1602液晶显示按键控制LED灯。 1、本设计采用STC89C51/52、AT89C51/52、AT89S51/52作为主控芯片,LCD1602液晶显示屏实时显示; 2、系统具有两种模式:录音…...

《软件项目管理》期末-复习题及参考答案
(1)赶工一个任务时,你应该关注( C ) A. 尽可能多的任务 B. 非关键任务 C. 加速执行关键路径上的任务 D. 通过成本最低化加速执行任务 (2)下列哪个不是项目管理计划的一部分?&#x…...
web vue 项目 Docker化部署
Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段: 构建阶段(Build Stage):…...
在软件开发中正确使用MySQL日期时间类型的深度解析
在日常软件开发场景中,时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志,到供应链系统的物流节点时间戳,时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库,其日期时间类型的…...

日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻
在如今就业市场竞争日益激烈的背景下,越来越多的求职者将目光投向了日本及中日双语岗位。但是,一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧?面对生疏的日语交流环境,即便提前恶补了…...
Ubuntu系统下交叉编译openssl
一、参考资料 OpenSSL&&libcurl库的交叉编译 - hesetone - 博客园 二、准备工作 1. 编译环境 宿主机:Ubuntu 20.04.6 LTSHost:ARM32位交叉编译器:arm-linux-gnueabihf-gcc-11.1.0 2. 设置交叉编译工具链 在交叉编译之前&#x…...

Opencv中的addweighted函数
一.addweighted函数作用 addweighted()是OpenCV库中用于图像处理的函数,主要功能是将两个输入图像(尺寸和类型相同)按照指定的权重进行加权叠加(图像融合),并添加一个标量值&#x…...
Golang dig框架与GraphQL的完美结合
将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用,可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器,能够帮助开发者更好地管理复杂的依赖关系,而 GraphQL 则是一种用于 API 的查询语言,能够提…...
使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装
以下是基于 vant-ui(适配 Vue2 版本 )实现截图中照片上传预览、删除功能,并封装成可复用组件的完整代码,包含样式和逻辑实现,可直接在 Vue2 项目中使用: 1. 封装的图片上传组件 ImageUploader.vue <te…...
【论文笔记】若干矿井粉尘检测算法概述
总的来说,传统机器学习、传统机器学习与深度学习的结合、LSTM等算法所需要的数据集来源于矿井传感器测量的粉尘浓度,通过建立回归模型来预测未来矿井的粉尘浓度。传统机器学习算法性能易受数据中极端值的影响。YOLO等计算机视觉算法所需要的数据集来源于…...

k8s业务程序联调工具-KtConnect
概述 原理 工具作用是建立了一个从本地到集群的单向VPN,根据VPN原理,打通两个内网必然需要借助一个公共中继节点,ktconnect工具巧妙的利用k8s原生的portforward能力,简化了建立连接的过程,apiserver间接起到了中继节…...

在WSL2的Ubuntu镜像中安装Docker
Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包: for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...