设计模式12-构建器

news2024/12/24 21:10:26

设计模式12-构建器

      • 由来和动机
      • 原理思想
      • 构建器模式的C++代码实现
      • 构建器模式中的各个组件详解
        • 1. 产品类(Product)
        • 2. 构建类(Builder)
        • 3. 具体构建类(ConcreteBuilder)
        • 4. 指挥者类(Director)
      • 各个类的相互关系
      • 示例运行
      • 特点及其应用
      • 总结
  • 问题
    • 解答
      • 模板方法模式(Template Method Pattern)
      • 构造者模式(Builder Pattern)
      • 主要区别
      • 为什么构造者模式不能完全替代模板方法模式
      • 结论

构建器模式(Builder Pattern)是一种设计模式,主要用于分步骤创建一个复杂的对象。这种模式的主要动机是将一个复杂对象的构建过程与其表示相分离,使得同样的构建过程可以创建不同的表示。构建器模式特别适合创建那些包含很多子对象或者配置步骤的对象。构建器模式也是对象创建模式的一种。

由来和动机

  • 在软件系统中有时候面临着一个复杂对象的创建工作。通常由各个部分的子对象用一定的算法构成。由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化但是将他们组合在一起的算法却相对稳定。
  • 那么如何应对这种变化呢?如何提供一种封装机制来隔离除复杂对象的各个部分的变化?从而保持系统中的稳定,构建算法不随着需求的变化而变化。构建器模式就是为了解决这个问题而诞生的一种模式。构建器模式的动机就是将对象的创建过程分解为多个步骤,并通过一个构建器对象来逐步构建最终的复杂对象。

原理思想

构建器模式的核心思想是将对象的构建过程封装在一个独立的构建器对象中。构建器对象提供了一系列方法,用于设置对象的各个部分。最后,通过一个build()方法来返回最终的复杂对象。这种方式不仅使对象的构建过程清晰易懂,还使得不同的构建器可以创建不同类型的对象。

构建器模式的C++代码实现

下面是一个详细的C++代码示例,演示如何使用构建器模式来构建一个复杂的对象。

#include <iostream>
#include <string>

// 产品类
class Product {
public:
    void setPartA(const std::string& partA) { partA_ = partA; }
    void setPartB(const std::string& partB) { partB_ = partB; }
    void setPartC(const std::string& partC) { partC_ = partC; }
    void show() const {
        std::cout << "Product parts: " << partA_ << ", " << partB_ << ", " << partC_ << std::endl;
    }
private:
    std::string partA_;
    std::string partB_;
    std::string partC_;
};

// 构建器基类
class Builder {
public:
    virtual ~Builder() {}
    virtual void buildPartA() = 0;
    virtual void buildPartB() = 0;
    virtual void buildPartC() = 0;
    virtual Product* getResult() = 0;
};

// 具体构建器
class ConcreteBuilder : public Builder {
public:
    ConcreteBuilder() { product_ = new Product(); }
    ~ConcreteBuilder() { delete product_; }
    void buildPartA() override { product_->setPartA("PartA"); }
    void buildPartB() override { product_->setPartB("PartB"); }
    void buildPartC() override { product_->setPartC("PartC"); }
    Product* getResult() override { return product_; }
private:
    Product* product_;
};

// 指挥者类
class Director {
public:
    void setBuilder(Builder* builder) { builder_ = builder; }
    Product* construct() {
        builder_->buildPartA();
        builder_->buildPartB();
        builder_->buildPartC();
        return builder_->getResult();
    }
private:
    Builder* builder_;
};

int main() {
    Director director;
    ConcreteBuilder builder;

    director.setBuilder(&builder);
    Product* product = director.construct();
    product->show();

    delete product;  // 需要记得删除Product对象以避免内存泄漏
    return 0;
}

构建器模式中的各个组件详解

在构建器模式中,主要包括以下四个角色:产品类、构建类(抽象构建器)、具体构建类和指挥者类。下面是对每个角色的详细说明。

1. 产品类(Product)

作用:产品类是要构建的复杂对象。它包含多个组成部分,这些部分可以通过构建器来逐步设置。

结构:产品类包含表示最终复杂对象的各个部分(通常是成员变量),以及设置这些部分的方法。

示例代码

// 产品类
class Product {
public:
    void setPartA(const std::string& partA) { partA_ = partA; }
    void setPartB(const std::string& partB) { partB_ = partB; }
    void setPartC(const std::string& partC) { partC_ = partC; }
    void show() const {
        std::cout << "Product parts: " << partA_ << ", " << partB_ << ", " << partC_ << std::endl;
    }
private:
    std::string partA_;
    std::string partB_;
    std::string partC_;
};

在这个示例中,Product类有三个部分(partA_partB_partC_),并提供了设置这些部分的方法。

2. 构建类(Builder)

作用:抽象构建器类定义了创建产品各个部分的方法接口,但不实现这些方法。它为具体构建器类提供了一个通用接口。

结构:抽象构建器类通常包含纯虚方法,这些方法用于创建产品的各个部分以及返回最终产品。

示例代码

// 构建器基类
class Builder {
public:
    virtual ~Builder() {}
    virtual void buildPartA() = 0;
    virtual void buildPartB() = 0;
    virtual void buildPartC() = 0;
    virtual Product* getResult() = 0;
};

在这个示例中,Builder类定义了四个纯虚方法,用于创建产品的各个部分和获取最终产品。

3. 具体构建类(ConcreteBuilder)

作用:具体构建器类实现了抽象构建器类定义的所有方法。它负责具体创建产品的各个部分,并返回最终产品。

结构:具体构建器类通常包含一个产品对象,并通过实现构建器接口的方法来设置产品的各个部分。

示例代码

// 具体构建器
class ConcreteBuilder : public Builder {
public:
    ConcreteBuilder() { product_ = new Product(); }
    ~ConcreteBuilder() { delete product_; }
    void buildPartA() override { product_->setPartA("PartA"); }
    void buildPartB() override { product_->setPartB("PartB"); }
    void buildPartC() override { product_->setPartC("PartC"); }
    Product* getResult() override { return product_; }
private:
    Product* product_;
};

在这个示例中,ConcreteBuilder类实现了所有抽象构建器的方法,并在构建产品的过程中设置产品的各个部分。

4. 指挥者类(Director)

作用:指挥者类负责控制构建过程。它按照一定的顺序调用构建器的方法来创建产品。

结构:指挥者类通常包含一个构建器对象,并提供一个方法来执行产品的构建过程。

示例代码

// 指挥者类
class Director {
public:
    void setBuilder(Builder* builder) { builder_ = builder; }
    Product* construct() {
        builder_->buildPartA();
        builder_->buildPartB();
        builder_->buildPartC();
        return builder_->getResult();
    }
private:
    Builder* builder_;
};

在这个示例中,Director类通过调用构建器的方法来构建产品,并返回最终的产品。

各个类的相互关系

  1. 产品类(Product):包含复杂对象的各个部分,并提供设置和显示这些部分的方法。
  2. 构建类(Builder):定义了创建产品各个部分的方法接口。
  3. 具体构建类(ConcreteBuilder):实现了抽象构建器类的方法,负责具体创建产品的各个部分。
  4. 指挥者类(Director):控制构建过程,通过调用构建器的方法按顺序创建产品。

示例运行

结合以上代码示例,运行以下main函数会生成并展示一个完整的产品:

int main() {
    Director director;
    ConcreteBuilder builder;

    director.setBuilder(&builder);
    Product* product = director.construct();
    product->show();

    delete product;  // 需要记得删除Product对象以避免内存泄漏
    return 0;
}

运行结果:

Product parts: PartA, PartB, PartC

通过以上详细说明和代码示例,可以清楚地理解构建器模式的结构和作用。构建器模式通过分步骤构建复杂对象,提高了代码的可读性和可维护性,并提供了更大的灵活性来创建不同类型的产品。

特点及其应用

特点:

  1. 分步骤构建:构建器模式将对象的构建过程分解为一系列步骤,每一步都可以独立进行。
  2. 可扩展性好:可以通过不同的具体构建器来创建不同的对象。
  3. 解耦构建过程和表示:构建器模式将对象的构建过程和表示解耦,使得相同的构建过程可以创建不同的表示。

应用场景:

  1. 复杂对象的创建:当创建一个复杂对象时,需要分步骤进行,而且每一步可能有不同的实现方式。
  2. 代码可读性和可维护性:当构建逻辑复杂且需要复用时,使用构建器模式可以使代码更清晰和易于维护。
  3. 配置对象:构建器模式适合用来创建那些包含多个配置项的对象,例如创建一个包含多个选项的配置文件。

总结

在这里插入图片描述

构建器模式是一种强大的设计模式,通过将对象的构建过程分离出来,使得复杂对象的创建变得更加灵活和可维护。在C++中,构建器模式可以通过定义一个产品类、一个抽象构建器类、一个具体构建器类以及一个指挥者类来实现。这种模式广泛应用于需要创建复杂对象的场景中,例如GUI库中的窗口构建、报表生成、游戏中的复杂角色创建等。

问题

在应用过程中是不是可以用构造者模式代替模版方法,完成变化与固定步骤的完全分离,如果不行为什么不行?

解答

构造者模式(Builder Pattern)和模板方法模式(Template Method Pattern)都是用于创建和管理对象的行为模式,但它们适用于不同的场景和问题。虽然它们都有助于将变化与固定步骤分离,但它们并不是可以互相替代的,因为它们解决的问题和实现方式不同。下面将详细说明两者的区别以及为什么不能直接用构造者模式代替模板方法模式。

模板方法模式(Template Method Pattern)

作用:模板方法模式定义了一个操作中的算法骨架,而将一些步骤的实现延迟到子类中。模板方法模式使得子类可以在不改变算法结构的情况下重新定义算法的某些步骤。

结构

  1. 抽象类:定义了一个模板方法,包含算法的骨架。模板方法调用了若干个抽象操作,这些操作在子类中实现。
  2. 具体类:实现了抽象类中定义的抽象操作。

示例代码

#include <iostream>

class AbstractClass {
public:
    void templateMethod() {
        stepOne();
        stepTwo();
        stepThree();
    }

    virtual ~AbstractClass() {}

protected:
    virtual void stepOne() = 0;
    virtual void stepTwo() = 0;
    virtual void stepThree() = 0;
};

class ConcreteClass : public AbstractClass {
protected:
    void stepOne() override {
        std::cout << "ConcreteClass: Step One" << std::endl;
    }

    void stepTwo() override {
        std::cout << "ConcreteClass: Step Two" << std::endl;
    }

    void stepThree() override {
        std::cout << "ConcreteClass: Step Three" << std::endl;
    }
};

int main() {
    ConcreteClass concreteClass;
    concreteClass.templateMethod();
    return 0;
}

运行结果:

ConcreteClass: Step One
ConcreteClass: Step Two
ConcreteClass: Step Three

构造者模式(Builder Pattern)

作用:构造者模式将一个复杂对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表示。

结构

  1. 产品类:表示要构建的复杂对象。
  2. 构建类(抽象构建器):定义了创建产品各个部分的方法接口。
  3. 具体构建类:实现了抽象构建器类的方法,负责具体创建产品的各个部分。
  4. 指挥者类:控制构建过程,通过调用构建器的方法按顺序创建产品。

示例代码(与之前相同):

// 产品类
class Product {
public:
    void setPartA(const std::string& partA) { partA_ = partA; }
    void setPartB(const std::string& partB) { partB_ = partB; }
    void setPartC(const std::string& partC) { partC_ = partC; }
    void show() const {
        std::cout << "Product parts: " << partA_ << ", " << partB_ << ", " << partC_ << std::endl;
    }
private:
    std::string partA_;
    std::string partB_;
    std::string partC_;
};

// 构建器基类
class Builder {
public:
    virtual ~Builder() {}
    virtual void buildPartA() = 0;
    virtual void buildPartB() = 0;
    virtual void buildPartC() = 0;
    virtual Product* getResult() = 0;
};

// 具体构建器
class ConcreteBuilder : public Builder {
public:
    ConcreteBuilder() { product_ = new Product(); }
    ~ConcreteBuilder() { delete product_; }
    void buildPartA() override { product_->setPartA("PartA"); }
    void buildPartB() override { product_->setPartB("PartB"); }
    void buildPartC() override { product_->setPartC("PartC"); }
    Product* getResult() override { return product_; }
private:
    Product* product_;
};

// 指挥者类
class Director {
public:
    void setBuilder(Builder* builder) { builder_ = builder; }
    Product* construct() {
        builder_->buildPartA();
        builder_->buildPartB();
        builder_->buildPartC();
        return builder_->getResult();
    }
private:
    Builder* builder_;
};

int main() {
    Director director;
    ConcreteBuilder builder;

    director.setBuilder(&builder);
    Product* product = director.construct();
    product->show();

    delete product;  // 需要记得删除Product对象以避免内存泄漏
    return 0;
}

主要区别

  1. 目的不同

    • 模板方法模式:用于定义一个算法的骨架,并允许子类实现其中的某些步骤。它的核心在于让子类决定某些步骤的具体实现,但算法的整体结构是固定的。
    • 构造者模式:用于分步骤创建一个复杂的对象。它的核心在于分离对象的构建过程和表示,使得同样的构建过程可以创建不同的表示。
  2. 实现方式不同

    • 模板方法模式:通过继承和方法重载实现。抽象类定义算法的骨架,具体类实现具体步骤。
    • 构造者模式:通过组合和逐步构建实现。构建器类定义了创建对象的各个部分的方法,指挥者类控制构建过程。
  3. 应用场景不同

    • 模板方法模式:适用于当一个算法的整体结构固定,但某些步骤的实现可以变化时。常用于框架和库中,定义一些通用算法,让用户实现具体步骤。
    • 构造者模式:适用于创建一个复杂对象,其构建过程可以分为多个步骤。常用于需要灵活创建复杂对象的场景。

为什么构造者模式不能完全替代模板方法模式

  1. 不同的设计意图:模板方法模式的设计意图是通过继承和方法重载来复用算法的整体结构,而构造者模式的设计意图是通过组合和分步骤构建来创建复杂对象。
  2. 适用场景不同:模板方法模式更适合于算法步骤固定但实现可以变化的场景,而构造者模式更适合于需要逐步创建复杂对象的场景。
  3. 实现方式不同:模板方法模式依赖于抽象类和具体类的继承关系,而构造者模式依赖于构建器和指挥者的组合关系。

结论

虽然构造者模式和模板方法模式都可以用于将变化与固定步骤分离,但它们的适用场景和实现方式不同,不能简单地互相替代。在设计模式的选择上,应根据具体问题的需求选择合适的模式,而不是试图用一种模式解决所有问题。

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

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

相关文章

实战:OpenFeign使用以及易踩坑说明

OpenFeign是SpringCloud中的重要组件&#xff0c;它是一种声明式的HTTP客户端。使用OpenFeign调用远程服务就像调用本地方法一样&#xff0c;但是如果使用不当&#xff0c;很容易踩到坑。 Feign 和OpenFeign Feign Feign是Spring Cloud组件中的一个轻量级RESTful的HTTP服务客…

rabbitmq生产与消费

一、rabbitmq发送消息 一、简单模式 概述 一个生产者一个消费者模型 代码 //没有交换机&#xff0c;两个参数为routingKey和消息内容 rabbitTemplate.convertAndSend("test1_Queue","haha");二、工作队列模式 概述 一个生产者&#xff0c;多个消费者&a…

C4D2024软件下载+自学C4D 从入门到精通【学习视频教程全集】+【素材笔记】

软件介绍与下载&#xff1a; 链接&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1n8cripcv6ZTx4TBNj5N04g?pwdhfg5 提取码&#xff1a;hfg5 基础命令的讲解&#xff1a; 掌握软件界面和基础操作界面。学习常用的基础命令&#xff0c;如建模、材质、灯光、摄像机…

设计模式-领域逻辑模式-结构映射模式

对象和关系之间的映射&#xff0c;关键问题在于二者处理连接的方式不同。 表现出两个问题&#xff1a; 表现方法不同。对象是通过在运行时&#xff08;内存管理环境或内存地址&#xff09;中保存引用的方式来处理连接的&#xff0c;关系数据库则通过创建到另外一个表的键值来处…

昇思25天学习打卡营第19天|munger85

Diffusion扩散模型 它并没有那么复杂&#xff0c;它们都将噪声从一些简单分布转换为数据样本&#xff0c;Diffusion也是从纯噪声开始通过一个神经网络学习逐步去噪&#xff0c;最终得到一个实际图像 def rearrange(head, inputs): b, hc, x, y inputs.shape c hc // head r…

大数据平台之HBase

HBase是一个高可靠性、高性能、面向列、可伸缩的分布式存储系统&#xff0c;是Apache Hadoop生态系统的重要组成部分。它特别适合大规模结构化和半结构化数据的存储和检索&#xff0c;能够处理实时读写和批处理工作负载。以下是对HBase的详细介绍。 1. 核心概念 1.1 表&#x…

TIA博途V19无法勾选来自远程对象的PUT/GET访问的解决办法

TIA博途V19无法勾选来自远程对象的PUT/GET访问的解决办法 TIA博途升级到V19之后,1500CPU也升级到了V3.1的固件,1200CPU升级到了V4.6.1的固件, 固件升级之后,又出现了很多问题,如下图所示,在组态的时候会多出一些东西, 添加CPU之后,在属性界面可以看到“允许来自远程对象…

第二讲:NJ网络配置

Ethernet/IP网络拓扑结构 一. NJ EtherNet/IP 1、网络端口位置 NJ的CPU上面有两个RJ45的网络接口,其中一个是EtherNet/IP网络端口(另一个是EtherCAT的网络端口) 2、网络作用 如图所示,EtherNet/IP网络既可以做控制器与控制器之间的通信,也可以实现与上位机系统的对接通…

python爬虫基础——Webbot库介绍

本文档面向对自动化网页交互、数据抓取和网络自动化任务感兴趣的Python开发者。无论你是初学者还是有经验的开发者&#xff0c;Webbot库都能为你的自动化项目提供强大的支持。 Webbot库概述 Webbot是一个专为Python设计的库&#xff0c;用于简化网页自动化任务。它基于Seleniu…

高速ADC模拟输入接口设计

目录 基本输入接口考虑 输入阻抗 输入驱动 带宽和通带平坦度 噪声 失真 变压器耦合前端 有源耦合前端网络 基本输入接口考虑 采用高输入频率、高速模数转换器(ADC)的系统设计是一 项具挑战性的任务。ADC输入接口设计有6个主要条件&#xff1a; 输入阻抗、输入驱动、带宽…

【RaspberryPi】树莓派系统UI优化

接上文&#xff0c;如何去定制一个树莓派的桌面系统&#xff0c;还是以CM4为例。 解除CM4上电USB无法使用问题 将烧录好的tf卡通过读卡器插入到电脑上&#xff0c;进入boot磁盘&#xff0c;里面有一个Config文件&#xff0c;双击用记事本打开&#xff0c;在【pi4】一栏里加入一…

农业农村大数据底座:实现智慧农业的关键功能

随着信息技术的快速发展&#xff0c;农业领域也在逐步实现数字化转型。农业农村大数据底座作为支持智慧农业发展的重要基础设施&#xff0c;承载了多种关键功能&#xff0c;为农业生产、管理和决策提供了前所未有的支持和可能性。 ### 1. 数据采集与监测 农业农村大数据底座首…

【k8s故障处理篇】calico-kube-controllers状态为“ImagePullBackOff”解决办法

【k8s故障处理篇】calico-kube-controllers状态为“ImagePullBackOff”解决办法 一、环境介绍1.1 本次环境规划1.2 kubernetes简介1.3 kubernetes特点二、本次实践介绍2.1 本次实践介绍2.2 报错场景三、查看报错日志3.1 查看pod描述信息3.2 查看pod日志四、报错分析五、故障处理…

【Docker】Docker Desktop - WSL update failed

问题描述 Windows上安装完成docker desktop之后&#xff0c;第一次启动失败&#xff0c;提示&#xff1a;WSL update failed 解决方案 打开Windows PowerShell 手动执行&#xff1a; wsl --set-default-version 2 wsl --update

使用C#手搓Word插件

WordTools主要功能介绍 编码语言&#xff1a;C#【VSTO】 1、选择 1.1、表格 作用&#xff1a;全选文档中的表格&#xff1b; 1.2、表头 作用&#xff1a;全选文档所有表格的表头【第一行】&#xff1b; 1.3、表正文 全选文档中所有表格的除表头部分【除第一行部分】 1.…

便携式自动气象站:科技赋能气象观测

便携式自动气象站&#xff0c;顾名思义&#xff0c;就是一款集成了多种气象传感器&#xff0c;能够自动进行气象观测和数据记录的设备。它体积小巧、重量轻&#xff0c;便于携带和快速部署&#xff0c;可以在各种环境下进行气象数据的实时监测。同时&#xff0c;通过内置的无线…

Flex布局中元素主轴上平均分布 多余的向左对齐

content&#xff1a;父元素 content-item: 子元素 主轴上子元素平均分布 .content {display: flex;flex-wrap: wrap;justify-content: space-between;.service-item {display: flex;flex-direction: column;justify-content: center;align-items: center;width: 80px;height:…

万字长文之分库分表里无分库分表键如何查询【后端面试题 | 中间件 | 数据库 | MySQL | 分库分表 | 其他查询】

在很多业务里&#xff0c;分库分表键都是根据主要查询筛选出来的&#xff0c;那么不怎么重要的查询怎么解决呢&#xff1f; 比如电商场景下&#xff0c;订单都是按照买家ID来分库分表的&#xff0c;那么商家该怎么查找订单呢&#xff1f;或是买家找客服&#xff0c;客服要找到对…

ubuntu一些好用的开发工具及其配置

1 终端模糊搜索fzf https://github.com/junegunn/fzf 输入某命令&#xff0c;比如 conda &#xff0c;按下ctrlR&#xff0c;会显示和该命令匹配的历史命令的列表 有了这个工具再也不用记忆太复杂的命令&#xff0c;只需要知道大概几个单词&#xff0c;输入即可搜索。 其搜索…

SSD基本架构与工作原理

SSD的核心由一个或多核心的CPU控制器、DRAM缓存以及多个NAND闪存芯片组成。CPU控制器负责管理所有读写操作&#xff0c;并通过DRAM缓存存储映射表等元数据&#xff0c;以加速寻址过程。 NAND闪存则是数据存储的实际介质&#xff0c;其组织结构从大到小依次为通道&#xff08;包…