AI Chat 设计模式:10. 组合模式

本文是该系列的第十篇,采用问答式的方式展开,问题由我提出,答案由 Chat AI 作出,灰色背景的文字则主要是我的一些思考和补充。

问题列表

  • Q.1 给我介绍一下组合模式
  • A.1
  • Q.2 好的,给我举一个组合模式的例子,使用 c++,要求容易理解
  • A.2
  • Q.3 在这个例子中,抽象类定义了所有可能的方法,包括文件类中其实不需要用的方法,这是透明组合模式吧?
  • A.3
  • Q.4 明白了,继续举一下安全组合模式的例子吧
  • A.4
  • Q.5 不错,对比一下安全组合模式和透明组合模式吧
  • A.5

Q.1 给我介绍一下组合模式

A.1

组合模式是一种结构型设计模式,它允许将对象组合成树形结构,以表示“部分-整体”的层次关系。通过组合模式,客户端可以统一对待单个对象和组合对象,而无需进行区分。

在组合模式中,有三种主要角色:

  1. 抽象构件(Component):它是组合中的对象声明接口,可以包含一些默认实现,比如添加、移除子构件等。它可以是抽象类或者接口。
  2. 叶子构件(Leaf):它表示叶子节点,即没有子节点的对象。叶子构件实现了抽象构件的接口。
  3. 组合构件(Composite):它表示有子节点的节点对象,可以添加和移除子节点。组合构件也实现了抽象构件的接口,并提供了管理子节点的操作

通过将叶子构件和组合构件进行递归组合,可以形成复杂的树状结构。这样,客户端就可以使用统一的方式对待单个对象和组合对象,而无需关心对象的具体类型。

使用组合模式的好处包括:

  1. 简化客户端代码:客户端无需区分叶子节点和组合节点,可以用统一的方式处理它们。
  2. 增加新节点方便:如果需要增加新的节点类型,只需要扩展抽象构件接口和相应的实现类即可,不会对现有代码造成影响。
  3. 简化树的操作:组合模式提供了统一的方式来操作整个树状结构,如遍历、搜索、添加、删除等。

总之,组合模式是一种以树状结构表示对象的模式,通过递归组合构件实现对整体和部分的处理,提供了一种灵活的方式来处理复杂的结构关系。

数据必须是以树形结构组织,才能使用组合模式

Q.2 好的,给我举一个组合模式的例子,使用 c++,要求容易理解

A.2

当然,下面给你一个使用C++实现组合模式的例子:假设我们要实现一个文件系统,其中包含文件和文件夹。文件夹可以包含文件或其他文件夹,而文件不能包含其他文件。

首先,我们定义一个抽象构件(Component)的基类,它包含了一些公共的接口:

class FileSystemComponent {
public:
    virtual void display() = 0;
    virtual void add(FileSystemComponent* component) = 0;
    virtual void remove(FileSystemComponent* component) = 0;
};

这个抽象类,定义了子类所有可能的操作。

然后,我们定义文件(File)类,它是叶子构件(Leaf):

#include <iostream>

class File : public FileSystemComponent {
private:
    std::string name;

public:
    File(std::string name) : name(name) {}

    void display() override {
        std::cout << "\tFile: " << name << std::endl;
    }

    void add(FileSystemComponent*) override {
        std::cout << "Cannot add to a file." << std::endl;
    }

    void remove(FileSystemComponent*) override {
        std::cout << "Cannot remove from a file." << std::endl;
    }
};

文件类,并不需要使用 add() remove() 方法。

接下来,我们定义文件夹(Folder)类,它是组合构件(Composite):

#include <iostream>
#include <vector>

class Folder : public FileSystemComponent {
private:
    std::string name;
    std::vector<FileSystemComponent*> components;

public:
    Folder(std::string name) : name(name) {}

    void display() override {
        std::cout << "Folder: " << name << std::endl;
        for (auto component : components) {
            component->display();
        }
    }

    void add(FileSystemComponent* component) override {
        components.push_back(component);
    }

    void remove(FileSystemComponent* component) override {
        auto it = std::find(components.begin(), components.end(), component);
        if (it != components.end()) {
            components.erase(it);
        }
    }
};

现在,我们可以使用这些类来构建文件系统的树形结构:

int main() {
    // 创建文件和文件夹
    FileSystemComponent* file1 = new File("file1.txt");
    FileSystemComponent* file2 = new File("file2.txt");
    FileSystemComponent* folder1 = new Folder("Folder 1");
    FileSystemComponent* folder2 = new Folder("Folder 2");

    // 组合文件夹
    folder1->add(file1);
    folder2->add(file2);
    folder2->add(folder1);

    // 显示文件系统
    folder2->display();

    // 清理内存
    delete folder2;

    return 0;
}

运行上面的代码,输出将会是:

Folder: Folder 2
	File: file2.txt
	Folder: Folder 1
		File: file1.txt

这个例子展示了如何使用组合模式来构建整个文件系统的树形结构。文件夹可以包含文件或其他文件夹,而文件只能是叶子节点。通过使用统一的方式来处理文件和文件夹,我们可以方便地对文件系统进行操作,如添加、删除和显示等。

我们看看这个例子的类图,所有构建都具有相同的接口。

在这里插入图片描述

Q.3 在这个例子中,抽象类定义了所有可能的方法,包括文件类中其实不需要用的方法,这是透明组合模式吧?

A.3

是的,这个例子展示的是透明组合模式。在透明组合模式中,抽象构件类定义了组合中所有可能的方法,包括那些在叶子构件中并不需要的方法。这样做的好处是客户端可以一致地对待所有的对象,无需关心它们的具体类型

在这个例子中,FileSystemComponent抽象基类包含了display()add()remove()方法,这些方法适用于文件和文件夹。由于文件和文件夹都是FileSystemComponent的子类,所以客户端可以统一地使用这些方法,而无需区分它们的具体类型。

如果我们将add()remove()这两个方法移除抽象基类,那么它们只能在组合构件(如文件夹)中使用,而在叶子构件(如文件)中不可用。这种称为安全组合模式。但透明组合模式更加灵活,因为它允许客户端像操作组合对象一样操作叶子对象,无需额外的类型检查。

总结起来,透明组合模式提供了更统一、更灵活的方式来操作组合结构,客户端可以使用一致的方法对待所有的对象,而无需关心对象的具体类型。

Q.4 明白了,继续举一下安全组合模式的例子吧

A.4

好的,下面是一个使用安全组合模式的例子:假设我们要构建一个组织结构图,其中包括公司、部门和员工。公司是顶层组合对象,部门是公司的子对象,员工是部门的子对象。员工不再具有添加子对象的能力。

首先,我们定义一个抽象构件(Component)的基类,它包含了共同的接口:

class OrgComponent {
public:
    virtual void display() = 0;
};

抽象构建,现在只定义公共接口。

然后,我们定义公司(Company)类,作为组合构件(Composite):

#include <iostream>
#include <vector>

class Company : public OrgComponent {
private:
    std::string name;
    std::vector<OrgComponent*> departments;

public:
    Company(std::string name) : name(name) {}

    void display() override {
        std::cout << "Company: " << name << std::endl;
        for (auto department : departments) {
            department->display();
        }
    }

    void addDepartment(OrgComponent* component) {
        departments.push_back(component);
    }

    void removeDepartment(OrgComponent* component) {
        auto it = std::find(departments.begin(), departments.end(), component);
        if (it != departments.end()) {
            departments.erase(it);
        }
    }
};

接下来,我们定义部门(Department)类,也是组合构件(Composite):

#include <iostream>
#include <vector>

class Department : public OrgComponent {
private:
    std::string name;
    std::vector<OrgComponent*> employees;

public:
    Department(std::string name) : name(name) {}

    void display() override {
        std::cout << "\tDepartment: " << name << std::endl;
        for (auto employee : employees) {
            employee->display();
        }
    }

    void addEmployee(OrgComponent* component) {
        employees.push_back(component);
    }

    void removeEmployee(OrgComponent* component) {
        auto it = std::find(employees.begin(), employees.end(), component);
        if (it != employees.end()) {
            employees.erase(it);
        }
    }
};

最后,我们定义员工(Employee)类,它是叶子构件(Leaf):

#include <iostream>

class Employee : public OrgComponent {
private:
    std::string name;

public:
    Employee(std::string name) : name(name) {}

    void display() override {
        std::cout << "\t\tEmployee: " << name << std::endl;
    }
};

现在,我们可以使用这些类来构建组织结构图:

int main() {
    // 创建公司
    OrgComponent* company = new Company("ABC Company");

    // 创建部门
    OrgComponent* sales = new Department("Sales Department");
    OrgComponent* hr = new Department("HR Department");

    // 创建员工
    OrgComponent* employee1 = new Employee("John");
    OrgComponent* employee2 = new Employee("Jane");
    OrgComponent* employee3 = new Employee("Alice");

    // 组合部门和员工
    static_cast<Company*>(company)->addDepartment(sales);
    static_cast<Company*>(company)->addDepartment(hr);
    static_cast<Department*>(sales)->addEmployee(employee1);
    static_cast<Department*>(sales)->addEmployee(employee2);
    static_cast<Department*>(hr)->addEmployee(employee3);

    // 显示组织结构图
    company->display();

    // 清理内存
    delete company;

    return 0;
}

运行上述代码,输出将会是:

Company: ABC Company
    Department: Sales Department
        Employee: John
        Employee: Jane
    Department: HR Department
        Employee: Alice

在这个例子中,抽象基类OrgComponent只定义了display()方法,而在组合构件(如CompanyDepartment)中定义了添加和删除子对象的方法。叶子构件Employee只需要实现自己所需的方法。

这种安全组合模式使得叶子构件没有暴露不适用的方法,而是将它们限制在组合构件中。这样做可以确保只有组合构件能够添加和删除子对象,而叶子构件无法进行这些操作。

安全的原因,叶子构建无法进行添加和删除操作,不会出现误用的情况。
看看类图,发现抽象类只定义了 display 这一个接口,其他各组件需要的接口只定义在自身类中,与透明组合模式明显不同。
在这里插入图片描述

Q.5 不错,对比一下安全组合模式和透明组合模式吧

A.5

安全组合模式和透明组合模式是组合模式的两种不同实现方式,它们在如何定义抽象构件(Component)以及在叶子构件(Leaf)和组合构件(Composite)中如何处理共同的接口方法上有所不同

  1. 安全组合模式(Safe Composite Pattern):

    • 在安全组合模式中,抽象构件(Component)接口中仅包含叶子构件和组合构件共有的方法不包含对子构件的添加和删除操作
    • 叶子构件和组合构件的接口都不同,叶子构件只实现自己需要的方法,而组合构件实现了管理子构件的方法
    • 优点:叶子构件不需要实现其它不相关的方法,更加清晰和简洁。
    • 缺点:客户端需要通过类型判断来调用适当的方法,不够统一。
  2. 透明组合模式(Transparent Composite Pattern):

    • 在透明组合模式中,抽象构件(Component)接口中包含了所有叶子构件和组合构件共有的方法,包括对子构件的添加和删除操作
    • 叶子构件和组合构件的接口完全一致,具有相同的方法集合。
    • 优点:客户端可以一致地对待所有的对象,无需关心对象的具体类型,使用更加简洁和方便。
    • 缺点:叶子构件需要实现一些不相关的方法,可能在具体实现中抛出异常或不做任何处理。

选择使用安全组合模式还是透明组合模式取决于具体情况,主要考虑以下因素:

  • 一致性需求:如果希望客户端统一对待所有对象,无需关心它们的具体类型和能力,透明组合模式更适合。如果要求叶子构件和组合构件的接口完全一致,使用安全组合模式。
  • 功能需求:如果叶子构件和组合构件之间有较大的差异,有许多方法只对组合构件有效,而对叶子构件无意义,使用安全组合模式可以保证叶子构件只需实现自己的功能。
  • 设计简洁性:安全组合模式可以更加清晰地表示叶子构件和组合构件之间的差异,每个类只需关注自己需要处理的方法。

总之,安全组合模式和透明组合模式都是组合模式的有效实现方式,根据具体需求和设计考虑选择合适的模式。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

css实现文字颜色渐变+阴影

效果 代码 <div class"top"><div class"top-text" text"总经理驾驶舱">总经理驾驶舱</div> </div><style lang"scss" scoped>.top{width: 100%;text-align: center;height: 80px;line-height: 80px;fo…

蓝桥杯上岸每日N题 第七期(小猫爬山)!!!

蓝桥杯上岸每日N题 第七期(小猫爬山)&#xff01;&#xff01;&#xff01; 同步收录 &#x1f447; 蓝桥杯上岸必背&#xff01;&#xff01;&#xff01;(第四期DFS) 大家好 我是寸铁&#x1f4aa; 冲刺蓝桥杯省一模板大全来啦 &#x1f525; 蓝桥杯4月8号就要开始了 &a…

【测试设计】性能测试工具选择:wrk?jmeter?locust?还是LR?

目录 前言 wrk 优点 缺点 jmeter 优点 缺点 locust 优点 缺点 总结 资料获取方法 前言 当你想做性能测试的时候&#xff0c;你会选择什么样的测试工具呢&#xff1f;是会选择wrk&#xff1f;jmeter&#xff1f;locust&#xff1f;还是loadrunner呢&#xff1f; 今…

opencv-29 Otsu 处理(图像分割)

Otsu 处理 Otsu 处理是一种用于图像分割的方法&#xff0c;旨在自动找到一个阈值&#xff0c;将图像分成两个类别&#xff1a;前景和背景。这种方法最初由日本学者大津展之&#xff08;Nobuyuki Otsu&#xff09;在 1979 年提出 在 Otsu 处理中&#xff0c;我们通过最小化类别内…

【二叉树进阶】二叉树的前中后序遍历(非递归迭代实现)

文章目录 1. 二叉树的前序遍历1.1 思路分析1.2 AC代码 2. 二叉树的中序遍历2.1 思路分析2.2 AC代码 3. 二叉树的后序遍历3.1 思路13.2 思路1AC3.3 思路23.4 思路2AC 1. 二叉树的前序遍历 题目链接: link 不用递归&#xff0c;用迭代算法如何实现对二叉树的前序遍历&#xff1f…

QT实现中英文键盘

使用Qt中实现中英文键盘&#xff0c;支持各种linux嵌入式设备。 实现思路&#xff1a;需要一个中文字体库&#xff0c;将字体库加载到一个Hash容器&#xff0c;字母和拼音作为key值&#xff0c;对应的中文作为value值。 核心代码&#xff1a; #include "UKeyBoard.h"…

【雕爷学编程】MicroPython动手做(28)——物联网之Yeelight 2

知识点&#xff1a;什么是掌控板&#xff1f; 掌控板是一块普及STEAM创客教育、人工智能教育、机器人编程教育的开源智能硬件。它集成ESP-32高性能双核芯片&#xff0c;支持WiFi和蓝牙双模通信&#xff0c;可作为物联网节点&#xff0c;实现物联网应用。同时掌控板上集成了OLED…

RISC-V基础之函数调用(三)保留寄存器(包含实例)

RISC-V将寄存器分为保留和非保留两类。保留寄存器是指在函数调用前后必须保持相同值的寄存器&#xff0c;因为调用者期望在调用后能够继续使用这些寄存器的值。保留寄存器包括s0到s11&#xff08;因此称为saved&#xff09;&#xff0c;sp和ra。非保留寄存器&#xff0c;也称为…

数据可视化(六)多个子图及seaborn使用

1.多个子图绘制 #绘制多个子图 #subplot&#xff08;*args&#xff0c;**kwargs&#xff09; 每个subplot函数只能绘制一个子图 #subplots&#xff08;nrows&#xff0c;ncols&#xff09; #fig_add_subplot(行&#xff0c;列&#xff0c;区域) #绘制子图第一种方式 plt.subp…

网络安全进阶学习第十课——MySQL手工注入

文章目录 一、MYSQL数据库常用函数二、MYSQL默认的4个系统数据库以及重点库和表三、判断数据库类型四、联合查询注入1、具体步骤&#xff08;靶场演示&#xff09;&#xff1a;1&#xff09;首先判断注入点2&#xff09;判断是数字型还是字符型3&#xff09;要判断注入点的列数…

网工内推 | 云计算工程师专场,CCNP/HCIP认证优先

01 弧聚科技 招聘岗位&#xff1a;网络工程师&#xff08;云计算方向&#xff09; 职责描述&#xff1a; 1、作为H3C初级云计算交付工程资源培养对象&#xff0c;需配合完成相关华三产品及服务规范培训。 2、培训赋能后&#xff0c;安排到H3C云项目交付中进行项目交付及驻场支…

【Nginx13】Nginx学习:HTTP核心模块(十)Types、AIO及其它配置

Nginx学习&#xff1a;HTTP核心模块&#xff08;十&#xff09;Types、AIO及其它配置 今天学习的内容也比较简单&#xff0c;主要的是 Types 相关的配置&#xff0c;另外还会了解一下 AIO 以及部分没有特别大的分类归属的配置指令的使用。后面的内容都是 HTTP 核心模块中比较小…

核心交换机新增了一个网段,现在下面PC可以获取地址访问内网 ,访问外网说DNS有问题不通

环境: SANGFOR AF 8.0.75 SANGFOR AC 13.0.47 H3C S6520-26Q-SI 问题描述: 1.在核心交换机上新规划了一个网段192.168.200.0/24,现在下面PC可以正常获取IP地址和DNS,正常访问内网服务和其它地址段IP ,访问外网说DNS有问题不通打不开网页 2.DNS解析失败,ping dns服务…

Kubernetes高可用集群二进制部署(二)ETCD集群部署

Kubernetes概述 使用kubeadm快速部署一个k8s集群 Kubernetes高可用集群二进制部署&#xff08;一&#xff09;主机准备和负载均衡器安装 Kubernetes高可用集群二进制部署&#xff08;二&#xff09;ETCD集群部署 Kubernetes高可用集群二进制部署&#xff08;三&#xff09;部署…

第四次作业 运维高级 安装tomcat8和部署jpress应用

1. 简述静态网页和动态网页的区别。 静态网页 静态网页是指存放在服务器文件系统中实实在在的HTML文件。当用户在浏览器中输入页面的URL&#xff0c;然后回车&#xff0c;浏览器就会将对应的html文件下载、渲染并呈现在窗口中。早期的网站通常都是由静态页面制作的。 静态网页…

银河麒麟V10 wireshark安装说明(断网离线)

下载离线安装包 链接&#xff1a;https://pan.baidu.com/s/11QFRmCGlIJrJaiKcHh9Hag?pwdu9wv 提取码&#xff1a;u9wv 安装步骤 tar zxvf wireshark.tar.gz cd wireshark sudo dpkt -i *.deb wireshark

构建vue项目配置和环境配置

目录 1、环境变量process.env配置2、vue package.json多环境配置vue-cli-service serve其他用法vue-cli-service build其他用法vue-cli-service inspect其他用法3、vue导出webpack配置4、配置打包压缩图片文件5、打包去掉多余css(由于依赖问题暂时未实现)6、打包去除console.…

【每日一题】—— C. Challenging Cliffs(Codeforces Round 726 (Div. 2))

&#x1f30f;博客主页&#xff1a;PH_modest的博客主页 &#x1f6a9;当前专栏&#xff1a;每日一题 &#x1f48c;其他专栏&#xff1a; &#x1f534; 每日反刍 &#x1f7e1; C跬步积累 &#x1f7e2; C语言跬步积累 &#x1f308;座右铭&#xff1a;广积粮&#xff0c;缓称…

Spring Boot 系列4 -- 统一功能处理

目录 前言 1. Spring AOP 用户统⼀登录验证的问题 1.1 自定义拦截器 1.2 配置拦截器并配置拦截的规则 1.3 拦截器的原理源码分析 2. 统一异常处理 2.1 实现统一异常处理 2.2 测试统一异常处理 3. 统一的数据格式返回 3.1 统⼀数据返回格式的实现 3.2 测试统一的数据返…

智慧水务和物联网智能水表在农村供水工程中的应用

摘 要&#xff1a;随着社会的进步和各项事业的飞速发展&#xff0c;人民生活水平的逐步提升&#xff0c;国家对农村饮水安全有了更高的要求&#xff0c;为了进一步提升农村供水服务的质量&#xff0c;利用现代化、信息化科学技术提升农村供水服务质量&#xff0c;提高用水管理效…