设计模式——建造者模式(生成器模式)

news2024/11/23 13:21:20

建造者模式(生成器模式)

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示的意图
用了建造者模式,那么用户就只需要指定需要构建的类型就可以得到它们,而具体构造的细节和过程不需要知道
在这里插入图片描述

概括地说,Builder是为创建一个Product对象的各个部件指定的抽象接口
而ConcreteBuilder是具体的建造者,实现Builder接口,构造和装配各个部件,Product是具体的产品
Director是指挥者,是构建一个使用Builder接口的对象。

  • 什么时候需要用到建造者模式?
    当我们需要创建一些复杂的对象,这些对象内部构建间的顺序通常是稳定的,但是对象内部的构建通常面临着复杂的变化

  • 实现方法

    1. 清晰地定义通用步骤, 确保它们可以制造所有形式的产品。 否则你将无法进一步实施该模式。
    2. 在基本生成器接口中声明这些步骤。
    3. 为每个形式的产品创建具体生成器类, 并实现其构造步骤。
    4. 考虑创建主管类。 它可以使用同一生成器对象来封装多种构造产品的方式。
    5. 客户端代码会同时创建生成器和主管对象。 构造开始前, 客户端必须将生成器对象传递给主管对象。 通常情况下, 客户端只需调用主管类构造函数一次即可。 主管类使用生成器对象完成后续所有制造任务。 还有另一种方式, 那就是客户端可以将生成器对象直接传递给主管类的制造方法。
    6. 只有在所有产品都遵循相同接口的情况下, 构造结果可以直接通过主管类获取。 否则, 客户端应当通过生成器获取构造结果。

基本代码如下

#include <iostream>
#include <vector>
#include <string>

using std::cout;

// 当产品非常复杂并且需要大量配置的时候,使用生成器模式非常有意义
// 各种构造器的结果可能并不总是遵循相同的接口

class Product1
{
public:
    std::vector<std::string> parts_;

    void ListParts() const
    {
        cout << "Product parts: ";
        for (size_t i = 0; i < parts_.size(); i++)
        {
            if (parts_[i] == parts_.back())
            {
                cout << parts_[i];
            }
            else
            {
                cout << parts_[i] << ", ";
            }
        }
        cout << "\n\n";
    }
};

// Builder 接口指定了创建 Product 对象不同部分的方法。
class Builder
{
public:
    virtual ~Builder(){};
    virtual void ProducePartA() const = 0;
    virtual void ProducePartB() const = 0;
    virtual void ProducePartC() const = 0;
};

// ConcreteBuilder 类遵循Builder类提供的接口,并提供建造步骤的具体实现。因此ConcreteBuilder应该有许多个,实现方法可以不同
class ConcreteBuilder1 : public Builder
{
private:
    // 一个新的构建器实例应该包含一个空白的产品对象,用于进一步的组装。
    Product1 *product;

public:
    ConcreteBuilder1()
    {
        this->Reset();
    }

    ~ConcreteBuilder1()
    {
        delete product;
    }

    void Reset()
    {
        this->product = new Product1();
    }

    // 所有生产步骤均使用同一产品实例
    void ProducePartA() const override
    {
        this->product->parts_.push_back("PartA1");
    }

    void ProducePartB() const override
    {
        this->product->parts_.push_back("PartB1");
    }

    void ProducePartC() const override
    {
        this->product->parts_.push_back("PartC1");
    }

    // 具体构建器应该提供自己的方法来检索结果。
    // 这是因为不同类型的构建器可能会创建完全不同的产品,这些产品不遵循相同的接口。
    // 因此,此类方法不能在基本构建器接口中声明(至少在静态类型编程语言中不能)

    // 通常,在将最终结果返回给客户端后,构建器实例应该准备好开始生产另一个产品。
    // 这就是为什么在 `getProduct` 方法主体末尾调用 reset 方法是一种常见做法。
    // 但是,这种行为不是强制性的,您可以让构建器等待来自客户端代码的明确 reset 调用,然后再处理之前的结果。

    // 一旦调用 GetProduct,此函数的用户就有责任释放此内存。使用智能指针来避免内存泄漏可能是一个更好的选择
    Product1 *GetProduct()
    {
        Product1 *result = this->product;
        this->Reset();
        return result;
    }
};

// Director 负责按照特定的顺序执行构建顺序,
class Director
{
private:
    Builder *builder;
    // Director 可与客户端代码传递给它的任何构建器实例配合使用。这样,客户端代码可能会改变新组装产品的最终类型。
public:
    void set_builder(Builder *builder)
    {
        this->builder = builder;
    }
    // Director 可以使用相同的构建步骤构建多个产品变体
    void BuildMinimalViableProduct()
    {
        this->builder->ProducePartA();
    }

    void BuildFullFeaturedProduct()
    {
        this->builder->ProducePartA();
        this->builder->ProducePartB();
        this->builder->ProducePartC();
    }
};

// 客户端代码创建一个构建器对象,将其传递给Director,然后启动构造过程。最终结果从构建器对象中检索。
void ClientCode(Director &director)
{
    ConcreteBuilder1 *builder = new ConcreteBuilder1();
    director.set_builder(builder);
    cout << "Standard basic product:\n";
    director.BuildMinimalViableProduct();

    Product1 *p = builder->GetProduct();
    p->ListParts();
    delete p;

    cout << "Standard full featured product:\n";
    director.BuildFullFeaturedProduct();

    p = builder->GetProduct();
    p->ListParts();
    delete p;

    // 也可以不使用Director类直接使用构造模式
    cout << "Custom product:\n";
    builder->ProducePartA();
    builder->ProducePartC();
    p = builder->GetProduct();
    p->ListParts();
    delete p;

    delete builder;
}

int main()
{
    Director *director = new Director();
    ClientCode(*director);
    delete director;
    return 0;
}

输出

Standard basic product:
Product parts: PartA1

Standard full featured product:
Product parts: PartA1, PartB1, PartC1

Custom product:
Product parts: PartA1, PartC1

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【复旦邱锡鹏教授《神经网络与深度学习公开课》笔记】神经元和人工神经网络

神经元 生物神经元&#xff1a; 平时处于抑制状态&#xff0c;当接受信息量达到一定程度后进入兴奋状态。 人工神经元&#xff1a; 一个人工神经元大致有两个步骤&#xff1a; 一是收集信息&#xff0c;如上图中 x 1 , ⋯ , x d x_1,\cdots,x_d x1​,⋯,xd​表示神经元可…

49.Python-web框架-Django解决多语言redirect时把post改为get的问题

目录 1.背景 2.思路 3.寻找 Find and Replace 4.再次运行程序&#xff0c;POST来了 5.小结 1.背景 昨天在练习一个Django功能时&#xff0c;把form的method设置为POST&#xff0c;但是实际提交时&#xff0c;一直是GET方法。最后发现这是与多语言相关&#xff0c;django前面…

一文详解:Git与SVN的对比与选择

多人协同开的时候面临着代码版本管理和同步问题&#xff0c;这个时候git和svn就就大显神威了&#xff0c;个别小伙伴对这俩不是很熟悉&#xff0c;贝格前端工场为大家解读下。 一、什么是git和svn 分布式版本控制和集中式版本控制是两种不同的版本控制系统架构。 Git 分布式…

【配置教程】Linux在企业端为何如此重要

目录 本节重点 先见一下什么是Linux 后台vs前台 企业为何选择使用Linux作为后台服务器 国内企业后台和用户使用Linux现状 1. IT服务器Linux系统应用领域 2. 嵌入式Linux系统应用领域 3. 个人桌面应用领域 Linux时代发展 版本更新 ​编辑 就个人找工作/能力提升来说…

12 款 Android 照片恢复应用程序列表

丢失难忘的照片总是令人痛苦的。如果软件崩溃或意外删除&#xff0c;Android 设备上的照片也可能会丢失。这时照片恢复应用程序就派上用场了。查看我们为 Android 收集的顶级照片恢复应用程序。 但是&#xff0c;您不会想为自己选择任何照片恢复应用程序。因此&#xff0c;我们…

为什么总选不到合适的安全数据交换系统?解决问题重点在这

安全数据交换系统对于企业而言&#xff0c;重要性不言而喻。企业业务开展离不开数据交换&#xff0c;只有数据流动起来&#xff0c;才能真正发挥价值&#xff0c;但数据流动的过程&#xff0c;涉及多个系统、多种环境、多个人员角色&#xff0c;因此&#xff0c;有较大的风险。…

redis03 补充 redis驱动模型:事件驱动

1.文件事件&#xff08;重点&#xff09;文件事件就是服务器对socket操作的抽象&#xff0c;Redis服务器通过监听并处理这些socket产生的文件事件&#xff0c;实现对客户端调用的响应 1.1 文件事务处理器的构成 1.2 IO多路复用 注&#xff1a; epoll是linux系统的底层IO多…

centos8 中文打印报错,解决

sudo yum install -y glibc-locale-source sudo localedef -c -f UTF-8 -i zh_CN zh_CN.UTF-8 sudo yum install -y fontconfig

vlc多媒体播放器(支持各种本地视频、网络视频、音频及摄像头直播地址)winform(支持全屏)自动适应x86、x64平台插件及重要代码

1、学习vlcControl1及libvlc.dll类方法(x86为例)最新v3.0.21 1.1 本博文以控件(vlcControl1)方式为主介绍 1.2 安装-引用,添加控件到窗体 using Vlc.DotNet.Forms; 1.3 窗体布局、编写代码 打开文件(以本地媒体文件为主)打开地址(以网络媒体地址为主)播放|暂停功能停…

C# WPF入门学习番外篇(二) —— C# WPF使用数据库创建注册登录界面

C# WPF入门学习番外篇&#xff08;二&#xff09; —— C# WPF使用数据库创建注册登录界面 在这篇番外篇博客中&#xff0c;我们将介绍如何在C# WPF应用程序中使用数据库来创建一个简单的注册和登录界面。通过本教程&#xff0c;你将学习到如何在WPF中与数据库进行交互&#xf…

提高SOA噪声系数和饱和功率的方法

----翻译自Kevin Carney, Robert Lennox等人撰写的文章 摘要 针对多触点体半导体光放大器&#xff08;SOA&#xff09;&#xff0c;使用速率方程模型研究了有源层纵向载流子密度分布特性。结果表明&#xff0c;噪声系数和饱和输出光功率都可以通过偏置电流分布进行优化。本文还…

Apache HttpClient总览

一、重大版本 Apache HttpClient 4.x 系列 • HttpClient 4.0&#xff08;发布于2008年左右&#xff09;&#xff1a;这是一个重要的里程碑&#xff0c;标志着HttpClient从Jakarta Commons项目转移到Apache HttpComponents项目。4.0版进行了大量的重构&#xff0c;引入了新…

Turbo Console Log自定义配置

写log太麻烦了&#xff1f;可以用下vscode中的Turbo Console Log的插件 因为vscode的其他快捷键可能会和这个插件产生冲突&#xff0c;所以可以从这里设置自定义不重复的快捷键。我这里用的shiftaltG用来生成log 我用的是显示第多少行和路径名 效果&#xff1a; 还有其他的…

从爱好到收入AI贴纸变现的五种途径,你尝试过几种?你会制作吗?

一、AI贴纸变现方式&#xff1a; 贴纸变现的方式主要包括以下几种&#xff1a; 1、广告变现 通过在小红书、公众号等可发图文的自媒体平台发布你制作的可爱贴纸&#xff0c;从而实现对可爱贴纸喜爱的人士观看并成为你的粉丝。粉丝达到一定数量即可接商业广告变现。 2、电商变…

js算法 计算每一列的平均值 求matrix数组对应列的平均值 组成一个新的数组 matrix数组有6行 如果某一行里的值是0则不纳入平均的分母里

let matrix [[18.95, 21.1, 0, 23, 0, 0, 0],[19, 25.3, 24.64, 0, 0, 0, 21.24],[22.18, 24.5, 20, 0, 26, 0, 0],[18.41, 19.05, 22.41, 27.67, 17, 0, 0],[14.86, 19.31, 0, 19.4, 18.71, 0, 25.04],[18.93, 19.53, 0, 0, 0, 0, 0] ];// 获取矩阵的列数 const numCols mat…

网络安全等级保护基本要求解读- 安全计算环境-应用系统和数据安全

概述 越来越多的企业用户已将核心业务系统转移到网络上&#xff0c;Web浏览器成为业 务系统的窗口&#xff0c;应用系统面临更多的安全威胁&#xff1b;并且由于各种原因使得其 存在较多的安全漏洞。 在此背景下&#xff0c;如何保障企业的应用安全&#xff0c;尤其是Web应用…

现场直击 | 飞凌嵌入式亮相2024上海国际嵌入式展

6月12日&#xff0c;2024上海国际嵌入式展&#xff08;embedded world China 2024&#xff09;在上海世博展览馆开幕。飞凌嵌入式亮相3号馆646展位&#xff0c;聚焦人工智能、智慧交通、工业互联网、智慧医疗、电力与储能等领域&#xff0c;旨在为全球客户带来一场技术与创新的…

MySQL第二种实现方式:现在有一个生产计划,甲乙丙3个品类共16个产品,生产时间6天,每天甲品类可以生产1张单,乙3张,丙1张,请用MySQL写出H列的效果

接上篇&#xff1a;链接: 现在有一个生产计划&#xff0c;甲乙丙3个品类共16个产品&#xff0c;生产时间6天&#xff0c;每天甲品类可以生产1张单&#xff0c;乙3张&#xff0c;丙1张&#xff0c;请用MySQL写出H列的效果 第二种写法&#xff1a; -- 使用WITH子句创建CTE WITH…

《500 Lines or Less》(4)Contingent: A Fully Dynamic Build System(构建系统)

介绍 构建系统长期以来一直是计算机编程中的标准工具。 标准 make 构建系统由其作者获得 ACM 软件系统奖&#xff0c;于 1976 年首次开发。它不仅允许您声明一个输出文件依赖于一个&#xff08;或多个&#xff09;输入文件&#xff0c;而且可以递归地执行此操作。例如&#xf…

MySQL(5)

聚合函数 GROUP BY 的使用 需求&#xff1a;查询各个部门的平均工资&#xff0c;最高工资SELECT department_id,AVG(salary),SUM(salary)FROM employeesGROUP BY department_id;需求&#xff1a;查询各个job_id的平均工资SELECT job_id,AVG(salary)FROM employeesGROUP BY jo…