设计模式之生成器模式
目录
1.简介
2.结构
3.使用场景
4.实例
5.优缺点
6.与其他模式的关系
7.总结
1.简介
生成器模式(Builder Pattern)是一种创建型设计模式,它允许你通过一步一步构建复杂对象,而不是通过一个包含大量参数的构造函数或方法。该模式特别适用于需要生成的对象具有多个可选属性,且这些属性的组合会导致大量构造函数重载的情况。
生成器模式通过将对象的构建过程分解成多个步骤,使得每一步都可以单独配置。这样不仅提高了代码的可读性和可维护性,还使得构建过程更加灵活和可扩展。
与其他创建型模式不同的是,生成器模式允许你在一个过程中创建一个对象,而无需等待所有部分都准备好。这种模式特别适用于需要构造不同表示或状态的复杂对象时。
2.结构
生成器模式的UML结构图如下所示:

1) 生成器(Builder)接口声明在所有类型生成器中通用的产品构造步骤。
2) 具体生成器(Concrete Builders)提供构造过程的不同实现。具体生成器也可以构造不遵循通用接口的产品。
3) 产品(Products)是最终生成的对象。由不同生成器构造的产品无需属于同一类层次结构或接口。
4) 主管(Director)类定义调用构造步骤的顺序,这样你就可以创建和复用特定的产品配置。
5) 客户端(Client)必须将某个生成器对象与主管类关联。一般情况下,你只需通过主管类构造函数的参数进行一次性关联即可。此后主管类就能使用生成器对象完成后续所有的构造任务。但在客户端将生成器对象传递给主管类制造方法时还有另一种方式。在这种情况下,你在使用主管类生产产品时每次都可以使用不同的生成器。
3.使用场景
1)使用生成器模式可避免“重叠构造函数(telescopicconstructor)”的出现。
假设你的构造函数中有十个可选参数,那么调用该函数会非常不方便;因此,你需要重载这个构造函数,新建几个只有较少参数的简化版。但这些构造函数仍需调用主构造函数,传递一些默认数值来替代省略掉的参数。生成器模式让你可以分步骤生成对象,而且允许你仅使用必须的步骤。应用该模式后,你再也不需要将几十个参数塞进构造函数里了。
2)当你希望使用代码创建不同形式的产品(例如石头或木头房屋)时,可使用生成器模式。
如果你需要创建的各种形式的产品,它们的制造过程相似且仅有细节上的差异,此时可使用生成器模式。基本生成器接口中定义了所有可能的制造步骤,具体生成器将实现这些步骤来制造特定形式的产品。同时,主管类将负责管理制造步骤的顺序。
3)使用生成器构造组合树或其他复杂对象。
当一个对象的构建过程非常复杂,涉及多个步骤和多个部件时,使用生成器模式可以将对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表示。
例如,构建一个汽车对象可能涉及设置车身、引擎、车轮和内饰等多个部件,每个部件都有多种选择。使用生成器模式,可以逐步构建汽车对象,并根据需要选择不同的部件。
4.实例
下面我将展示一个稍微复杂一些的应用场景,即构建一个包含多个可选组件的计算机配置对象。
首先,我们定义一个Computer类,它代表我们要构建的计算机对象:
#include <iostream>
#include <string>
#include <vector>
#include <memory>class Computer {
private:std::string caseType;std::string cpu;std::string gpu;std::string ram;std::string storage;std::vector<std::string> peripherals;// 禁止直接实例化Computer(const std::string& caseType, const std::string& cpu, const std::string& gpu,const std::string& ram, const std::string& storage,const std::vector<std::string>& peripherals): caseType(caseType), cpu(cpu), gpu(gpu), ram(ram), storage(storage), peripherals(peripherals) {}friend class ComputerBuilder;public:void display() const {std::cout << "Case Type: " << caseType << "\n";std::cout << "CPU: " << cpu << "\n";std::cout << "GPU: " << gpu << "\n";std::cout << "RAM: " << ram << "\n";std::cout << "Storage: " << storage << "\n";std::cout << "Peripherals: ";for (const auto& peripheral : peripherals) {std::cout << peripheral << " ";}std::cout << "\n";}
};
接下来,我们定义一个ComputerBuilder类,用于逐步构建Computer对象:
class ComputerBuilder {
protected:std::string caseType;std::string cpu;std::string gpu;std::string ram;std::string storage;std::vector<std::string> peripherals;public:virtual ~ComputerBuilder() = default;ComputerBuilder& setCaseType(const std::string& caseType) {this->caseType = caseType;return *this;}ComputerBuilder& setCpu(const std::string& cpu) {this->cpu = cpu;return *this;}ComputerBuilder& setGpu(const std::string& gpu) {this->gpu = gpu;return *this;}ComputerBuilder& setRam(const std::string& ram) {this->ram = ram;return *this;}ComputerBuilder& setStorage(const std::string& storage) {this->storage = storage;return *this;}ComputerBuilder& addPeripheral(const std::string& peripheral) {peripherals.push_back(peripheral);return *this;}virtual Computer build() const = 0;
};
注意,这里我们将ComputerBuilder定义为一个抽象类,并声明了一个纯虚函数build,这样我们就可以有不同的具体实现来构建不同类型的计算机。
现在,我们定义一个具体的GamingComputerBuilder类来构建游戏计算机:
class GamingComputerBuilder : public ComputerBuilder {
public:GamingComputerBuilder() {// 可以设置一些默认配置setCaseType("ATX Mid Tower");setCpu("Intel i9");// ... 其他默认配置}Computer build() const override {return Computer(caseType, cpu, gpu, ram, storage, peripherals);}
};
在GamingComputerBuilder中,我们可以设置一些默认配置,或者覆盖父类的方法来提供特定的配置选项。
最后,我们定义一个ComputerDirector类(可选)来管理构建过程,并展示如何使用这些类来构建一个计算机对象:
class ComputerDirector {
private:ComputerBuilder* builder;public:ComputerDirector(ComputerBuilder* builder) : builder(builder) {}void setBuilder(ComputerBuilder* builder) {this->builder = builder;}Computer constructGamingComputer() {builder->setGpu("NVIDIA RTX 3090");builder->setRam("32GB DDR4");builder->setStorage("2TB SSD");builder->addPeripheral("Gaming Keyboard");builder->addPeripheral("Gaming Mouse");// ... 其他配置return builder->build();}
};int main() {GamingComputerBuilder gamingBuilder;ComputerDirector director(&gamingBuilder);// 或者使用自定义的构建步骤// gamingBuilder.setCaseType("Custom Case");// ... 其他设置// Computer customComputer = gamingBuilder.build();Computer gamingComputer = director.constructGamingComputer();gamingComputer.display();return 0;
}
在这个例子中,我们展示了如何使用生成器模式来构建一个包含多个可选组件的计算机配置对象。通过定义抽象的ComputerBuilder类和具体的GamingComputerBuilder类,我们可以灵活地构建不同类型的计算机,并且可以通过ComputerDirector类来管理构建过程(尽管在这个例子中ComputerDirector是可选的,但在更复杂的场景中它可能会很有用)。
5.优缺点
优点:
● 你可以分步创建对象,暂缓创建步骤或递归运行创建步骤。
● 生成不同形式的产品时,你可以复用相同的制造代码。
● 单一职责原则。你可以将复杂构造代码从产品的业务逻辑中分离出来。
由于该模式需要新增多个类,因此代码整体复杂程度会有所增加。
6.与其他模式的关系
与工厂模式的关系
- 工厂模式:主要用于生产各种对象,这些对象通常是兄弟类,继承自同一个基类。兄弟子类通过实现基类接口,展现不同的行为,并由工厂函数创建。然而,工厂模式在创建对象时并不关注构造细节,因此在处理复杂对象的生成时可能会显得力不从心。
- 生成器模式:同样用于对象的生成,但更侧重于构造细节,增加了额外的构建流程,以便处理复杂对象的构建需求。生成器模式将对象构造的代码从产品类中抽取出来,放在一个名为生成器的独立对象中,并允许通过不同的生成器来创建不同形式的对象。
- 对比:相较于工厂模式,生成器模式提供了更灵活和详细的对象构造方式。当对象相对简单时,可以使用工厂模式;而当对象复杂且需要详细配置时,生成器模式则更为适用。
与抽象工厂模式的关系
- 抽象工厂模式:专注于生成一系列相关对象,但它在对象构造复杂时,其能力也有限。抽象工厂模式通过定义一个接口,使得客户端可以创建相关或依赖对象的家族,而无需明确指定具体类。
- 生成器模式:虽然也用于创建对象,但更侧重于对象的逐步构造和细节配置。生成器模式允许开发者通过不同的生成器来创建具有不同配置和形式的对象。
- 联系与区别:两者都提供了对象的创建方式,但抽象工厂模式更侧重于对象的家族创建,而生成器模式则更侧重于对象的逐步构造和细节配置。此外,抽象工厂模式通常会立即返回产品,而生成器模式则允许在获取产品前执行一些额外的构造步骤。
与其他模式的关系
- 桥接模式:生成器模式可以与桥接模式结合使用,其中主管类负责抽象工作,而各种不同的生成器负责实现工作。这种结合可以使得对象构造更加灵活和可扩展。
- 原型模式:原型模式通过复制现有对象来创建新对象,而生成器模式则通过逐步构造来创建对象。虽然两者在对象创建方式上有所不同,但在某些情况下,它们可以相互补充,共同实现复杂的对象创建需求。
- 单例模式:抽象工厂、生成器和原型模式都可以用单例来实现,以确保对象的唯一性和全局可访问性。然而,这种实现方式并不常见,因为它可能会增加系统的复杂性和维护成本。
7.总结
生成器模式通过将复杂对象的构建过程与表示分离,使得同样的构建过程可以创建不同的表示。它允许逐步构建对象,通过调用生成器对象的方法来添加或修改对象的部件。
相关文章:
设计模式之生成器模式
目录 1.简介 2.结构 3.使用场景 4.实例 5.优缺点 6.与其他模式的关系 7.总结 1.简介 生成器模式(Builder Pattern)是一种创建型设计模式,它允许你通过一步一步构建复杂对象,而不是通过一个包含大量参数的构造函数或方法。该…...
python学opencv|读取图像(三)放大和缩小图像
【1】引言 前序已经学习了常规的图像读取操作和图像保存技巧,相关文章链接为: python学opencv|读取图像-CSDN博客 python学opencv|读取图像(二)保存彩色图像-CSDN博客 今天我们更近一步,学习放大和缩小图像的技巧&…...
1 数据库(上):MySQL的概述和安装、SQL简介、IDEA连接数据库使用图形化界面
文章目录 前言一、数据库相关的概念二、MySQL概述1 MySQL的安装和配置2 MySQL登录、退出(1)mysql -uroot -p1234 或者mysql -uroot -p ---- 登录(2)exit或者quit ---- 退出 3 远程登录服务器上的MySQL命令mysql -hip地址 -P3306 -…...
C++初阶—类与对象(中篇)
第一章:类的6个默认成员函数 如果一个类中什么成员都没有,简称为空类。 空类中真的什么都没有吗?并不是,任何类在什么都不写时,编译器会自动生成以下6个默认成员函数。 默认成员函数:用户没有显式实现&a…...
Leetcode15. 三数之和(HOT100)
链接 一般这种三数之和,四数之和都使用双指针,复杂度最优,次一级可使用哈希表。前者要求有序,后者空间上有花费。 题目: 题目要求答案中不能出现重复vector,比如{-1 1 0}和{-1 0 1}; 这两个…...
Oracle数据库小白备忘
sqlplus相关 导入sql文件 在sqlplus中,导入一个sql文件,是使用或者start。 如当前目录下有一个hello.sql,则可以使用 hello.sql 或者 start hello.sql 来进行导入,功能类似于mysql里面的source。 退出编辑模式 当使用sqlplus…...
DDR4与DDR3服务器内存的关键区别有哪些?
内存作为服务器性能的关键组件之一,已经经历了从DDR3到DDR4的过渡。DDR4内存相较于DDR3在多个方面有所提升,包括速度、带宽、功耗以及数据传输效率等。然而,尽管DDR4内存在性能上占有优势,DDR3内存依然在一些特定场景中得到了广泛…...
Linux: shell: bash: set -x;调试使用
man bash set -x -x After expanding each simple command, for command, case command, select command, or arithmetic for command, display the expanded value of PS4, followed by the command and its expanded arguments or associated word list. 这个可以帮助将变量…...
Hadoop生态圈框架部署 伪集群版(五)- HBase伪分布式部署
文章目录 前言一、Hbase伪分布式部署(手动部署)1. 下载Hbase2. 上传安装包3. 解压HBase安装包4. 配置HBase配置文件4.1 修改hbase-env.sh配置文件4.2 修改hbase-site.xml配置文件4.3 修改regionservers配置文件4.4 删除hbase中slf4j-reload4j-1.7.33.jar…...
自定义指令,全局,局部,注册
让输入框自动获取焦点(每次刷新自动获取焦点) <template><div><h3>自定义指令</h3><input ref"inp" type"text"></div> </template><script> export default {mounted(){this.$refs.inp.focus…...
静坐修心.
文章目录 打坐的历史文化渊源东方的起源与传承西方的接受与演变现代生活中的打坐 盘腿坐对身体的影响促进脊椎健康改善呼吸系统功能增强消化系统机能改善血液循环调节神经系统错误姿势及其他潜在危害 盘腿坐对心理的作用促进内心平静与放松提升自我觉察与内在探索培养专注力与精…...
设计模式c++(一)
文章目录 一、面向对象设计原则二、模版方法三、策略模式四、观察者模式五、装饰模式六、桥模式七、工厂方法_Factory Method八、抽象工厂_Abstract Factory九、原型模式十、构建器_builder十一、单件模式_Singleton十二、享元模式_Flyweight 一、面向对象设计原则 设计模式的…...
核密度估计——从直方图到核密度(核函数)估计_带宽选择
参考 核密度估计(KDE)原理及实现-CSDN博客 机器学习算法(二十一):核密度估计 Kernel Density Estimation(KDE)_算法_意念回复-GitCode 开源社区 引言 在统计学中,概率密度估计是一种重要的方法࿰…...
Vant UI Axure移动端元件库:提升移动端原型设计效率
UI框架的选择对于提升开发效率和用户体验至关重要。Vant UI,作为一款基于Vue.js的轻量、可靠的移动端组件库,自2017年开源以来,凭借其丰富的组件库、良好的性能以及广泛的兼容性,在移动端开发领域崭露头角,赢得了众多开…...
如何用 JavaScript 操作 DOM 元素?
如何用 JavaScript 操作 DOM 元素?——结合实际项目代码示例讲解 在前端开发中,DOM(文档对象模型)操作是与页面交互的核心。通过 DOM 操作,开发者可以动态地修改页面内容、响应用户交互、控制样式等。JavaScript 提供…...
【Ubuntu】URDC(Ubuntu远程桌面助手)安装、用法,及莫名其妙进入全黑模式的处理
1、简述 URDC是Ubuntu远程桌面助手的简称。 它可以: 实时显示桌面:URDC支持通过Windows连接至Ubuntu设备(包括x86和ARM架构,例如Jetson系列、树莓派等)的桌面及光标。远程操控双向同步剪切板多客户端连接:同一Ubuntu设备最多可同时被三台Windows客户端连接和操控,适用于…...
ES-DSL查询
term查询 因为精确查询的字段搜是不分词的字段,因此查询的条件也必须是不分词的词条。查询时,用户输入的内容跟自动值完全匹配时才认为符合条件。如果用户输入的内容过多,反而搜索不到数据。 语法说明: // term查询 GET /index…...
npm 设置镜像
要在npm中设置镜像,你可以使用npm config命令。以下是设置npm镜像的步骤: 临时使用淘宝镜像: npm --registry https://registry.npmmirror.com install package-name 永久设置镜像: npm config set registry https://registry…...
SpringMvc完整知识点一
SpringMVC概述 定义 SpringMVC是一种基于Java实现MVC设计模型的轻量级Web框架 MVC设计模型:即将应用程序分为三个主要组件:模型(Model)、视图(View)和控制器(Controller)。这种分离…...
STM32G4系列MCU双ADC多通道数据转换的应用
目录 概述 1 STM32Cube配置项目 1.1 基本参数配置 1.1.1 ADC1参数配置 1.1.2 ADC2参数配置 1.2 项目软件架构 2 功能实现 2.1 ADC转换初始化 2.2 ADC数据组包 3 测试函数 3.1 Vofa数据接口 3.2 输入数据 4 测试 4.1 ADC1 通道测试 4.2 ADC2 通道测试 概述 本文…...
深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录
ASP.NET Core 是一个跨平台的开源框架,用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录,以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…...
<6>-MySQL表的增删查改
目录 一,create(创建表) 二,retrieve(查询表) 1,select列 2,where条件 三,update(更新表) 四,delete(删除表…...
【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密
在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...
Python爬虫(二):爬虫完整流程
爬虫完整流程详解(7大核心步骤实战技巧) 一、爬虫完整工作流程 以下是爬虫开发的完整流程,我将结合具体技术点和实战经验展开说明: 1. 目标分析与前期准备 网站技术分析: 使用浏览器开发者工具(F12&…...
sqlserver 根据指定字符 解析拼接字符串
DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...
根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:
根据万维钢精英日课6的内容,使用AI(2025)可以参考以下方法: 四个洞见 模型已经比人聪明:以ChatGPT o3为代表的AI非常强大,能运用高级理论解释道理、引用最新学术论文,生成对顶尖科学家都有用的…...
CRMEB 中 PHP 短信扩展开发:涵盖一号通、阿里云、腾讯云、创蓝
目前已有一号通短信、阿里云短信、腾讯云短信扩展 扩展入口文件 文件目录 crmeb\services\sms\Sms.php 默认驱动类型为:一号通 namespace crmeb\services\sms;use crmeb\basic\BaseManager; use crmeb\services\AccessTokenServeService; use crmeb\services\sms\…...
LRU 缓存机制详解与实现(Java版) + 力扣解决
📌 LRU 缓存机制详解与实现(Java版) 一、📖 问题背景 在日常开发中,我们经常会使用 缓存(Cache) 来提升性能。但由于内存有限,缓存不可能无限增长,于是需要策略决定&am…...
Golang——7、包与接口详解
包与接口详解 1、Golang包详解1.1、Golang中包的定义和介绍1.2、Golang包管理工具go mod1.3、Golang中自定义包1.4、Golang中使用第三包1.5、init函数 2、接口详解2.1、接口的定义2.2、空接口2.3、类型断言2.4、结构体值接收者和指针接收者实现接口的区别2.5、一个结构体实现多…...
从“安全密码”到测试体系:Gitee Test 赋能关键领域软件质量保障
关键领域软件测试的"安全密码":Gitee Test如何破解行业痛点 在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的"神经中枢"。从国防军工到能源电力,从金融交易到交通管控,这些关乎国计民生的关键领域…...
