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

news2024/11/17 3:27:50

本文是该系列的第八篇,采用问答式的方式展开,问题由我提出,答案由 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.coloradmin.cn/o/804537.html

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

相关文章

android存储4--初始化.emulated设备的挂载

android版本&#xff1a;android-11.0.0_r21http://aospxref.com/android-11.0.0_r21 android手机的挂载非常复杂。这篇文章针对emulated存储&#xff0c;介绍它的挂载过程。 一、为什么emulted存储要用很复杂的挂载方式 1&#xff0c; emulted存储是什么 android早期&#…

QTday4(鼠标事件和键盘事件/QT实现连接TCP协议)

笔记 #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QDebug> #include <QTcpServer>//服务器类 #include <QTcpSocket>//客户端类 #include <QMessageBox> #include <QList>//链表容器QT_BEGIN_NAMESPACE namespace Ui …

【数据结构】实验十二:图 查找

实验十二 图查找 一、实验目的与要求 1&#xff09;掌握拓扑排序的应用&#xff1b; 2&#xff09;掌握查找的概念和算法&#xff1b; 3&#xff09;掌握查找的基本原理以及各种算法的实现&#xff1b; 4&#xff09;掌握查找的应用。 二、实验内容 1. 用邻接表建立一…

安卓:百度地图开发(超详细)

一、百度地图介绍 百度地图SDK是一套供开发者使用的软件开发工具包&#xff08;SDK&#xff09;&#xff0c;用于在Android应用程序中集成和使用百度地图功能。通过使用百度地图SDK&#xff0c;开发者可以实现在自己的应用中显示地图、获取定位信息、进行搜索、导航等功能。 百…

机器视觉初步14:相机标定原理及应用

相机标定是指通过已知的相机参数&#xff0c;解算相机内部参数矩阵和外部参数矩阵。 文章目录 1.为什么要标定&#xff1f;2.工业场景中常见的标定方法2.1. 使用棋盘格标定板&#xff08;Checkerboard Markers&#xff09;2.2 使用相机自标定2.3. 使用三维物体标定2.4.九孔标…

MATLAB与ROS联合仿真——控制类功能模块介绍

1、Keyboard Control &#xff08;1&#xff09;输入参数&#xff1a;无 &#xff08;2&#xff09;输出参数&#xff1a;Speed Factor为输出的速度系数&#xff08;1代表前行&#xff0c;0停止&#xff0c;-1代表后退&#xff09;&#xff0c;Turn Factor为输出的舵机系数&am…

excel绘制折线图或者散点图

一、背景 假如现在通过代码处理了一批数据&#xff0c;想看数据的波动情况&#xff0c;是不是还需要写个pyhon代码&#xff0c;读取文件&#xff0c;绘制曲线&#xff0c;看起来也简单&#xff0c;但是还有更简单的方法&#xff0c;就是直接生成csv文件&#xff0c;csv文件就是…

windows11打不开任务管理器,

目录 第一章、win11系统任务管理器打不开&#xff1f;第二章、解决方式修改注册表 友情提醒&#xff1a; 先看文章目录&#xff0c;大致了解文章知识点结构&#xff0c;点击文章目录可直接跳转到文章指定位置。 第一章、win11系统任务管理器打不开&#xff1f; Win11任务管理…

达梦数据库DEM监控部署

1、安装达梦8数据库 在192.168.1.253 windows机器上安装达梦8数据库 解压dm8_setup_win64_ent_8.1.1.56_20200115.iso安装包运行安装程序setup.exe 数据库安装信息&#xff1a; 数据库名:CBDM 实例名:CBDM 数据库目录:D:\dmdbms\data 端口:5236 控制文件: D:\dmdbms\data\CBDM\…

技术复盘(5)--git

技术复盘--git 资料地址原理图安装配置基本命令分支命令对接gitee练习:远程仓库操作 资料地址 学习地址-B站黑马&#xff1a;https://www.bilibili.com/video/BV1MU4y1Y7h5 git官方&#xff1a;https://git-scm.com/ gitee官网&#xff1a;https://gitee.com/ 原理图 说明&am…

Vue-Router基本使用

1 安装&#xff1a; vue2项目要安装vue-router3版本 npm i vue-router3 2 src下创建router目录&#xff0c;router文件夹下创建index.js 在vue.config.js中 配置src路径别名 3 在main.js中引入 4 在app.vue中配置 5 即可看到内容

十分钟配置好Neovim go开发环境(其他语言一样)

文章目录 前言仓库地址用法快捷键问题反馈 前言 这篇文章的目的是为了分享下我自己的Neovim配置。 本人是Golang程序员&#xff0c;最开始使用的IDE是JetBrains Goland。有一说一这个ide适配度很高&#xff0c;认识的很多人都使用这个。但是它也有几个对我来说的缺点&#xf…

JSP 结构和指令

JSP 结构 网络服务器需要一个 JSP 引擎&#xff0c;也就是一个容器来处理 JSP 页面。容器负责截获对 JSP 页面的请求。本教程使用内嵌 JSP 容器的 Apache 来支持 JSP 开发。 JSP 容器与 Web 服务器协同合作&#xff0c;为JSP的正常运行提供必要的运行环境和其他服务&#xff…

低代码:告别繁琐,提速软件开发

一、前言 数字化进程加速&#xff0c;对于软件开发效率和成本的要求在不断提高。与此同时&#xff0c;低代码技术的出现为这项高成本的人力开发提供了便捷。目前&#xff0c;低代码已广泛应用于各行各业&#xff0c;帮助企业提高应用开发效率、降低开发成本、提高软件质量&…

Vue 3:玩一下web前端技术(四)

前言 本章内容为VUE开发环境的使用与相关使用讨论。 上一篇文章地址&#xff1a; Vue 3&#xff1a;玩一下web前端技术&#xff08;三&#xff09;_Lion King的博客-CSDN博客 下一篇文章地址&#xff1a; &#xff08;暂无&#xff09; 一、开发环境的使用 1、汉化VScod…

提升设计技能,教你玩转CAD中的辅助命令

大家好&#xff0c;今天先来了解一下CAD软件中的视图操作。 CAD设计中的辅助命令是实现精准和高效设计的关键要素。熟练掌握并正确运用CAD中的各类辅助命令&#xff0c;对于设计师们来说至关重要。本文将为您提供如何正确使用CAD中的辅助命令的实用指南&#xff0c;帮助您优化…

动手学深度学习——实战Kaggle比赛:预测房价(代码详解+调参优化)

目录 1. 下载和缓存数据集2. Kaggle3. 访问和读取数据集4. 数据预处理5. 训练6. K折交叉验证7. 模型选择8. 提交Kaggle预测9. 调参优化 1. 下载和缓存数据集 数据集百度云&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/14CVZBjmlKA_c3MYNFLSvbg?pwdpysi 提取码&…

脚手架(vue-cli)的安装详细教程

首先要下载node.js 下载 | Node.js 中文网 (nodejs.cn)https://nodejs.cn/download/ 大家根据自己的系统来选择哪个&#xff0c;我是Windows系统&#xff0c;所以选择红色箭头所指的安装包去安装&#xff01;&#xff01;&#xff01; 接下来双击安装&#xff01;&#xff01;…

Docker 如何助您成为数据科学家

一、说明 在过去的 5 年里&#xff0c;我听到了很多关于 docker 容器的嗡嗡声。似乎我所有的软件工程朋友都在使用它们来开发应用程序。我想弄清楚这项技术如何使我更有效率&#xff0c;但我发现网上的教程要么太详细&#xff1a;阐明我作为数据科学家永远不会使用的功能&#…