实战设计模式之建造者模式

概述

        在实际项目中,我们有时会遇到需要创建复杂对象的情况。这些对象可能包含多个组件或属性,而且每个组件都有自己的配置选项。如果直接使用构造函数或前面介绍的工厂方法来创建这样的对象,可能会导致以下两个严重问题。

        1、参数过多。当一个类有很多可选参数时,构造函数可能会变得非常庞大,难以维护。此外,调用方必须记住哪些参数是必需的,哪些是可选的,这在一定程度上增加了出错的风险。

        2、代码冗余。如果每次创建对象都需要传递大量的参数,那么代码将会变得冗长、复杂且难以理解。即使有些参数有默认值,调用者仍然需要为所有参数提供值。

        相比之下,建造者模式提供了更好的解决方案。它通过引入一个专门的建造者类来逐步构建对象,直到所有的必要部分都被设置完毕。这样不仅可以简化对象的创建过程,还可以提高代码的可读性和可维护性。

        餐厅点餐是运用建造者模式的一个典型例子:菜单上有多种套餐组合,但每个套餐的具体内容可能不同;服务员会根据我们的选择,逐步记录下我们想要的食物和饮料,最后再把这些信息传递给厨房。在这个过程中,服务员扮演了“导演”的角色,而每道菜的厨师则是具体的“建造者”,他们负责按照订单的要求制作食物。

基本原理

        建造者模式的核心思想是:将一个复杂对象的构建过程与其表示分离,从而使得同样的构建过程可以创建不同的表示。它提供了一种分步骤构造复杂对象的方法,避免了大型构造函数带来的问题,并且提高了代码的可读性和可维护性。建造者模式包括如下四个核心组件。

        1、产品类。代表要创建的复杂对象,它可以是一个具体的产品实例,也可以是一系列相关属性的集合。产品类通常包含多个组成部分,每个部分都有自己的配置选项。

        2、抽象建造者。定义了创建产品各个部分的接口,它是所有具体建造者的基类或接口,声明了一系列用于组装产品的抽象方法。这些方法定义了如何构建产品的不同部分,但不涉及具体的实现逻辑。

        3、具体建造者。实现了抽象建造者接口,提供了构建产品的具体实现。每个具体建造者都对应一种特定的产品配置或类型,负责按照预定规则组装出完整的产品。通常情况下,具体建造者还会提供一个类似GetResult的方法来获取最终构建的产品对象。

        4、导演类。导演类负责协调各个具体建造者的执行顺序,它不直接参与产品的构建,而是调用建造者的方法来组织构建过程。它可以根据不同的需求选择合适的建造者,并控制构建的流程。

        基于上面的核心组件,建造者模式的实现主要有以下五个步骤。

        1、定义产品类。创建一个代表最终产品的类,它包含了所有与产品相关的属性。

        2、定义抽象建造者。定义一个接口或抽象类,声明一组用于构建产品的方法。这些方法定义了如何设置产品的各个部分,但不涉及具体的实现细节。

        3、实现具体建造者。根据需要创建多个具体建造者类,每个类都实现了抽象建造者接口。每个具体建造者负责按照特定的规则,组装出完整的产品,并提供GetResult方法来返回最终构建的产品对象。

        4、创建导演类。定义一个导演类,它负责调用具体建造者的方法来组织构建过程。导演类不直接参与产品的构建,而是充当协调者的角色,确保构建过程按照预期进行。

        5、编写应用层代码。在应用层代码中,首先选择合适的具体建造者并将其传递给导演类。导演类会根据预设的构建流程调用建造者的方法,逐步构建出最终的产品对象。最后,应用层可以从建造者那里获取到完全配置好的产品实例。

实战解析

        在下面的实战代码中,我们使用建造者模式模拟了餐厅点餐的应用场景。

        首先,我们定义了产品类COrder。COrder类代表最终的订单对象,它包含了主菜、饮料、甜点以及额外添加的项目。

        然后,我们定义了抽象建造者COrderBuilder。COrderBuilder是一个接口或抽象类,声明了一系列用于设置订单各个部分的方法。每个具体建造者都将实现这些方法,以按照特定规则组装出完整的订单。

        接下来,我们实现了具体建造者CStandardMealBuilder和CVegetarianMealBuilder,分别表示创建标准套餐和素食套餐。每个具体建造者都实现了COrderBuilder接口,并提供了构建特定类型订单的具体逻辑。

        我们还创建了导演类CWaiter,它负责协调各个建造者的执行顺序。它不直接参与订单的构建,而是调用建造者的方法来组织构建过程,这样可以确保构建过程的一致性和灵活性。

        最后,在main函数中,我们首先选择了CStandardMealBuilder来构建一个标准套餐,并通过CWaiter类来指导构建过程。紧接着,我们选择了CVegetarianMealBuilder来构建一个素食套餐。最后,我们打印了两个订单的具体内容,并进行了清理工作。

#include <iostream>
#include <string>
#include <vector>using namespace std;// 定义产品类:订单对象
class COrder
{
public:void SetMainDish(const string& strDish){m_strMainDish = strDish;}void SetDrink(const string& strDrink){m_strDrink = strDrink; }void SetDessert(const string& strDessert){m_strDessert = strDessert;}void AddExtra(const string& strExtra){m_vctExtras.push_back(strExtra);}void Print() const{cout << "Your order contains: " << endl;if (!m_strMainDish.empty()){cout << "Main dish: " << m_strMainDish << endl;}if (!m_strDrink.empty()){cout << "Drink: " << m_strDrink << endl;}if (!m_strDessert.empty()){cout << "Dessert: " << m_strDessert << endl;}if (!m_vctExtras.empty()){cout << "Extras: " << endl;for (const auto& extra : m_vctExtras){cout << "  " << extra << endl;}}}private:string m_strMainDish;               // 主菜string m_strDrink;                  // 饮料string m_strDessert;                // 甜点vector<string> m_vctExtras;         // 额外项
};// 定义抽象建造者
class COrderBuilder
{
public:COrderBuilder(){m_pOrder = new COrder();}virtual ~COrderBuilder() {}virtual void BuildMainDish() = 0;virtual void BuildDrink() = 0;virtual void BuildDessert() = 0;virtual void AddExtras() = 0;virtual COrder* GetResult(){return m_pOrder; }protected:COrder* m_pOrder;
};// 实现具体建造者:标准套餐建造者
class CStandardMealBuilder : public COrderBuilder
{
public:void BuildMainDish() override{m_pOrder->SetMainDish("Roast Chicken");}void BuildDrink() override{m_pOrder->SetDrink("Cola");}void BuildDessert() override{m_pOrder->SetDessert("Ice Cream");}void AddExtras() override{m_pOrder->AddExtra("Potato Fries");}
};// 实现具体建造者:素食套餐建造者
class CVegetarianMealBuilder : public COrderBuilder
{
public:void BuildMainDish() override{m_pOrder->SetMainDish("Vegetable Curry");}void BuildDrink() override{m_pOrder->SetDrink("Lemonade");}void BuildDessert() override{m_pOrder->SetDessert("Fruit Salad");}void AddExtras() override{m_pOrder->AddExtra("Breadsticks");}
};// 创建导演类:服务员
class CWaiter
{
public:void Construct(COrderBuilder* pBuilder){pBuilder->BuildMainDish();pBuilder->BuildDrink();pBuilder->BuildDessert();pBuilder->AddExtras();}
};int main()
{CWaiter waiter;// 选择标准套餐COrderBuilder* pBuilder = new CStandardMealBuilder();waiter.Construct(pBuilder);COrder* pStandardOrder = pBuilder->GetResult();cout << "***** Standard Meal *****" << endl;pStandardOrder->Print();cout << endl;delete pBuilder;// 选择素食套餐pBuilder = new CVegetarianMealBuilder();waiter.Construct(pBuilder);COrder* pVegetarianOrder = pBuilder->GetResult();cout << "***** Vegetarian Meal *****" << endl;pVegetarianOrder->Print();delete pBuilder;// 清理delete pStandardOrder;delete pVegetarianOrder;return 0;
}

总结

        对于那些需要很多参数的对象创建,尤其是这些参数大多数是可选的时候,使用建造者模式可以让代码更加易读、易于维护。相比长串的构造函数参数列表,建造者模式提供了更自然的接口来逐步设定属性。

        但建造者模式也引入了额外的类(即具体的建造者),因此会稍微增加系统的复杂度和整体大小。对于简单的对象创建,这种模式可能显得过于繁琐。如果要添加新的产品,就需要修改现有的建造者接口或创建新的建造者子类,这可能会影响现有代码的稳定性。另外,如果建造者负责多个不同类型的对象创建,可能会导致建造者类变得臃肿,承担过多责任,从而违背了单一职责原则。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/501554.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

我的博客年度之旅:感恩、成长与展望

目录 感恩有你 技能满点 新年新征程 嘿&#xff0c;各位技术大佬、数码潮咖还有屏幕前超爱学习的小伙伴们&#xff01;当新年的钟声即将敲响&#xff0c;我们站在时光的交汇点上&#xff0c;回首过往&#xff0c;满心感慨&#xff1b;展望未来&#xff0c;豪情满怀。过去的这…

聆听音乐 1.5.9 | 畅听全网音乐,支持无损音质下载

聆听音乐手机版是面向广大音乐爱好者的移动应用程序&#xff0c;用户可以随时随地通过手机享受丰富的音乐资源。它提供了多种魅力功能&#xff0c;让用户在手机上畅享更舒适的音乐体验&#xff0c;每位用户都能享受精彩纷呈的收听体验。此外&#xff0c;软件还支持无损音质音乐…

GRU-PFG:利用图神经网络从股票因子中提取股票间相关性

“MCI-GRU: Stock Prediction Model Based on Multi-Head Cross-Attention and Improved GRU” 论文地址&#xff1a;https://arxiv.org/pdf/2410.20679 摘要 金融市场因复杂性及大数据时代的来临&#xff0c;使得准确预测股票走势变得尤为重要。传统的时序分析模型&#xff0…

Leetcode 第426场周赛分析总结

3370. 仅含置位位的最小整数 AC代码 class Solution { public:int smallestNumber(int n) {int x 1;while (x - 1 < n) {x << 1;}return x - 1;} };分析总结 也可以先直接获取n的长度&#xff0c;然后计算得到&#xff0c;这样时间复杂度由O(logn)优化为O(1) 在C…

在 SQL 中,区分 聚合列 和 非聚合列(nonaggregated column)

文章目录 1. 什么是聚合列&#xff1f;2. 什么是非聚合列&#xff1f;3. 在 GROUP BY 查询中的非聚合列问题示例解决方案 4. 为什么 only_full_group_by 要求非聚合列出现在 GROUP BY 中&#xff1f;5. 如何判断一个列是聚合列还是非聚合列&#xff1f;6. 总结 在 SQL 中&#…

C++ —— 智能指针

内存泄漏 什么是内存泄漏&#xff0c;内存泄漏的危害 什么是内存泄漏&#xff1a;内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内 存泄漏并不是指内存在物理上的消失&#xff0c;而是应用程序分配某段内存后&#xff0c;因为设计错误&#xff0c;失去…

Ethernet 系列(12)-- 基础学习::SOME/IP

目录 1. SOME/IP简介&#xff1a; 1.1 什么是SOME/IP&#xff1a; 1.2 什么时候使用SOME/IP&#xff1a; 2. SOME/IP的特点&#xff1a; 2.1 序列化&#xff1a; 2.2 远程过程调用&#xff08;RPC&#xff09;: 2.3 服务发现&#xff1a; 2.4 发布/订阅&#xff1a; 2.5 UDP消息…

UE5.3 虚幻引擎 Windows插件开发打包(带源码插件打包、无源码插件打包)

0 引言 随着项目体量的增大&#xff0c;所有代码功能都放一起很难管理。所以有什么办法可以将大模块划分成一个个小模块吗。当然有&#xff0c;因为虚幻引擎本身就遇到过这个问题&#xff0c;他的解决办法就是使用插件的形式开发。 例如&#xff0c;一个团队开发了文件I/O模块插…

自学记录鸿蒙API 13:实现多目标识别Object Detection

起步&#xff1a;什么叫多目标识别&#xff1f; 无论是生活中的动物识别、智能相册中的场景分类&#xff0c;还是工业领域的检测任务&#xff0c;都能看到多目标识别的身影。这次&#xff0c;我决定通过学习HarmonyOS最新的Object Detection API&#xff08;API 13&#xff09…

光伏安装在屋顶:安全、环保还是潜在威胁?

随着环保意识的增强和科技的进步&#xff0c;光伏发电作为一种可再生能源技术&#xff0c;正逐渐走进千家万户。然而&#xff0c;随着光伏板的普及&#xff0c;关于其在屋顶安装是否对人体有害的疑问也随之而来。 一、光伏发电的基本原理 光伏发电是利用半导体界面的光生伏特效…

被催更了,2025元旦源码继续免费送

“时间从来不会停下&#xff0c;它只会匆匆流逝。抓住每一刻&#xff0c;我们才不会辜负自己。” 联系作者免费领&#x1f496;源&#x1f496;码。 三联支持&#xff1a;点赞&#x1f44d;收藏⭐️留言&#x1f4dd;欢迎留言讨论 更多内容敬请期待。如有需要源码可以联系作者免…

MYsql--------ubantu中安装mysql

在Ubuntu平台上下载、启动和关闭MySQL的方法如下&#xff1a; 下载安装MySQL 更新软件包列表&#xff1a;打开终端&#xff0c;输入以下命令&#xff0c;确保软件包列表是最新的。sudo apt update安装MySQL服务器&#xff1a;执行以下命令安装MySQL服务器。在安装过程中&…

pygame飞机大战

飞机大战 1.main类2.配置类3.游戏主类4.游戏资源类5.资源下载6.游戏效果 1.main类 启动游戏。 from MainWindow import MainWindow if __name__ __main__:appMainWindow()app.run()2.配置类 该类主要存放游戏的各种设置参数。 #窗口尺寸 import random import pygame WIND…

Flutter中的网络请求图片存储为缓存,与定制删除本地缓存

Flutter中的网络请求图片存储为缓存&#xff0c;与定制删除本地缓存 1&#xff1a;封装请求图片函数 2&#xff1a;访问的图片都会转为本地缓存&#xff0c;当相同的请求url&#xff0c;会在本地调用图片 3&#xff1a;本地缓存管理【windows与andriod已经测试】【有页面】【有…

无线AP安装注意事项

现在的办公楼、酒店等项目中都设计含有网络无线覆盖这一项&#xff0c;在项目实施中&#xff0c;往往采用的是便捷并且后期便于网络无线设备管理的无线ap设备&#xff0c;作为前端无线信号的覆盖。在具体安装无线AP过程中&#xff0c;我们必须要注意以下几点才能保证项目实施完…

Golang的容器编排实践

Golang的容器编排实践 一、Golang中的容器编排概述 作为一种高效的编程语言&#xff0c;其在容器编排领域也有着广泛的运用。容器编排是指利用自动化工具对容器化的应用进行部署、管理和扩展的过程&#xff0c;典型的容器编排工具包括Docker Swarm、Kubernetes等。在Golang中&a…

计算机毕业设计Django+Tensorflow音乐推荐系统 音乐可视化 卷积神经网络CNN LSTM音乐情感分析 机器学习 深度学习 Flask

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

C# 在PDF中添加和删除水印注释 (Watermark Annotation)

目录 使用工具 C# 在PDF文档中添加水印注释 C# 在PDF文档中删除水印注释 PDF中的水印注释是一种独特的注释类型&#xff0c;它通常以透明的文本或图片形式叠加在页面内容之上&#xff0c;为文档添加标识或信息提示。与传统的静态水印不同&#xff0c;水印注释并不会永久嵌入…

分析服务器 systemctl 启动gozero项目报错的解决方案

### 分析 systemctl start beisen.service 报错 在 Linux 系统中&#xff0c;systemctl 是管理系统和服务的主要工具。当我们尝试重启某个服务时&#xff0c;如果服务启动失败&#xff0c;systemctl 会输出错误信息&#xff0c;帮助我们诊断和解决问题。 本文将通过一个实际的…

Dubbo扩展点加载机制

加载机制中已经存在的一些关键注解&#xff0c;如SPI、©Adaptive> ©Activateo然后介绍整个加载机制中最核心的ExtensionLoader的工作流程及实现原理。最后介绍扩展中使用的类动态编译的实 现原理。 Java SPI Java 5 中的服务提供商https://docs.oracle.com/jav…