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 2File: file2.txtFolder: Folder 1File: 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 CompanyDepartment: Sales DepartmentEmployee: JohnEmployee: JaneDepartment: HR DepartmentEmployee: 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.mzph.cn/news/12435.shtml

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

相关文章

idea导入maven项目问题

问题产生原因&#xff1a; ①idea加载maven项目&#xff0c;如果网络不通畅&#xff0c;会在maven仓库中产生一个文件&#xff0c;如下图所示: ②当网络通畅时&#xff0c;在下载就会因为此文件导致无法下载正确的maven依赖 解决方案&#xff1a; ①打开maven仓库的根目录 ②…

ts中声明引入未使用的报错——解决方案

在编写ts项目的时候&#xff0c;经常会出现如下报错&#xff1a; 导入声明中的所有导入都未使用 这是因为导入的模块暂时没有使用&#xff0c;ts给的一个提示信息 解决方案&#xff1a; 在ts.config.json中 把noUnusedLocals 设置为false即可 {"compilerOptions"…

【雕爷学编程】Arduino动手做(175)---机智云ESP8266开发板模块4

37款传感器与执行器的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止这37种的。鉴于本人手头积累了一些传感器和执行器模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&am…

【UE5 多人联机教程】03-创建游戏

效果 步骤 打开“UMG_MainMenu”&#xff0c;增加创建房间按钮的点击事件 添加如下节点 其中&#xff0c;“FUNL Fast Create Widget”是插件自带的函数节点&#xff0c;内容如下&#xff1a; “创建会话”节点指游戏成功创建一个会话后&#xff0c;游戏的其他实例即可发现&am…

微服务体系<1>

我们的微服务架构 我们的微服务架构和单体架构的区别 什么是微服务架构 微服务就是吧我们传统的单体服务分成 订单模块 库存模块 账户模块单体模块 是本地调用 从订单模块 调用到库存模块 再到账户模块 这三个模块都是调用的同一个数据库 这就是我们的单体架构微服务 就是…

Training-Time-Friendly Network for Real-Time Object Detection 论文学习

1. 解决了什么问题&#xff1f; 目前的目标检测器很少能做到快速训练、快速推理&#xff0c;并同时保持准确率。直觉上&#xff0c;推理越快的检测器应该训练也很快&#xff0c;但大多数的实时检测器反而需要更长的训练时间。准确率高的检测器大致可分为两类&#xff1a;推理时…

银河麒麟安装mysql数据库(mariadb)-银河麒麟安装JDK-银河麒麟安装nginx(附安装包)

银河麒麟离线全套安装教程&#xff08;手把手教程&#xff09; 1.银河麒麟服务器系统安装mysql数据库&#xff08;mariadb&#xff09; 2.银河麒麟桌面系统安装mysql数据库&#xff08;mariadb&#xff09; 3.银河麒麟服务器系统安装JDK 4.银河麒麟桌面系统安装JDK 5.银河麒麟…

青大数据结构【2021】

一、单选&#xff08;17&#xff01;&#xff09; 根据中序遍历得到降序序列可以知道&#xff0c;每个结点的左子树的结点的值比该结点的值小&#xff0c;因为没有重复的关键字&#xff0c;所以拥有最大值的结点没有左子树。 二、简答 三、分析计算 四、算法分析 3.迪杰斯特拉…

windows安装Elasticsearch8.9.0

官网解压安装好路径&#xff08;非中文&#xff0c;无空格&#xff09; 可参考 言之有李LAX csdn http://t.csdn.cn/S2oju本人使用jdk17 修改配置elasticsearch.yml xpack.security.enabled: false xpack.security.http.ssl:enabled: false直接点击bin\elasticsearch.bat…

Unity 工具之 NuGetForUnity 包管理器,方便在 Unity 中的进行包管理的简单使用

Unity 工具之 NuGetForUnity 包管理器&#xff0c;方便在 Unity 中的进行包管理的简单使用 目录 Unity 工具之 NuGetForUnity 包管理器&#xff0c;方便在 Unity 中的进行包管理的简单使用 一、简单介绍 二、NuGetForUnity 的下载导入 Unity 三、NuGetForUnity 在 Unity 的…

LeetCode 75 第十二题(11)盛最多水的容器

目录 题目: 示例: 分析: 代码: 题目: 示例: 分析: 配合着示例给出的图片我们可以得知找出盛水最多的容器是什么意思,给一个数组,找出数组中两个元素能围成的最大的矩阵面积是多少. 比较直观的想法是套两层for循环暴力解出来,但是这题是中等难度题,一般中等题是没法用暴力得…

pinia在vue3中的使用

总结&#xff1a; 在store文件夹中建一个pinia的文件userStore.js 1.要想使用pinia必须先引入defineStore 这里我们使用es6的模块化语法导出的 import { defineStore } from pinia 2.然后使用export const useUserStore defineStore(user,{}) defineStore 方法有两个参数&…

论文分享:PowerTCP: Pushing the Performance Limits of Datacenter Networks

1 原论文的题目&#xff08;中英文&#xff09;、题目中包含了哪些关键词&#xff1f;这些关键词的相关知识分别是什么&#xff1f; 题目&#xff1a;PowerTCP: Pushing the Performance Limits of Datacenter Networks PowerTCP&#xff1a;逼近数据中心的网络性能极限 2 论…

vue+Element项目中v-for循环+表单验证

如果在Form 表单里有通过v-for动态生成&#xff0c;如何设置验证呢&#xff1f; <el-form ref"ruleFormRef" :model"ruleForm" status-icon :rules"rules" label-width"120px"class"demo-ruleForm" hide-required-aster…

在EF Core中为数据表按列加密存储

假设有User表 public class User : Entity<int> {public int Id { get; set; }public string UserName { get; set; }public string Name { get; set; }public string IdentificationNumber { get; set; } }其中有身份证号码IdentificationNumber列&#xff0c;需要加密…

虚拟机(VMware)安装Linux(Ubuntu)安装教程

清华大学开源网站镜像站网址&#xff1a;清华大学开源软件镜像站 | Tsinghua Open Source Mirror 进入之后在搜索框中搜索“ubuntu” 直接点击箭头所指的蓝色字体“ubuntu-20.04.1-desktop-amd64.iso”即可下载

[Ubuntu 22.04] containerd配置HTTP方式拉取私仓Harbor

文章目录 1. 基础环境配置2. Docker安装3. 部署Harbor&#xff0c;HTTP访问4. 部署ContainerD5. 修改docker配置文件&#xff0c;向harbor中推入镜像6. 配置containerd6.1. 拉取镜像验证6.2. 推送镜像验证 1. 基础环境配置 [Ubuntu 22.04] 安装K8S基础环境准备脚本 2. Docker安…

听说 Spring Bean 的创建还有一条捷径?

文章目录 1. resolveBeforeInstantiation1.1 applyBeanPostProcessorsBeforeInstantiation1.2 applyBeanPostProcessorsAfterInitialization1.3 案例 2. 源码实践2.1 切面 Bean2.2 普通 Bean 在 Spring Bean 的创建方法中&#xff0c;有如下一段代码&#xff1a; AbstractAutow…

详解rocketMq通信模块升级构想

本文从开发者的角度深入解析了基于netty的通信模块, 并通过简易扩展实现微服务化通信工具雏形, 适合于想要了解netty通信框架的使用案例, 想了解中间件通信模块设计, 以及微服务通信底层架构的同学。希望此文能给大家带来通信模块架构灵感。 概述 网络通信是很常见的需求&#…

什么是自动化测试?

什么是自动化测&#xff1f; 做测试好几年了&#xff0c;真正学习和实践自动化测试一年&#xff0c;自我感觉这一个年中收获许多。一直想动笔写一篇文章分享自动化测试实践中的一些经验。终于决定花点时间来做这件事儿。 首先理清自动化测试的概念&#xff0c;广义上来讲&#…