C++【组合模式】

简单介绍

组合模式是一种结构型设计模式, 只有在可以将对象拆分为【树状结构】的情况下使用。并且像使用独立对象一样使用它们。
常用于表示与图形打交道的用户界面组件或代码的层次结构。

基础理解

订单中可能包括各种产品, 这些产品放置在盒子中, 然后又被放入一层又一层更大的盒子中。 整个结构看上去像是一棵倒过来的树。

Q:为什么要用组合模式 ?
A:在一个树状的结构中,叶子节点与父节点的距离会很远,使用循环语句一个一个遍历会大大增加复杂度。所以需要使用一个通用接口将叶子节点与父节点实现交互。

Q:如何设计组合模式 ?
A:对于一个字节点,直接返回你需要的东西,对于父节点,则会遍历其所有的子节点,最后 " 汇总 " 结果给其父节点
组合模式以递归方式处理对象树中的所有项目


优点:无需了解构成树状结构的对象的具体类,无论复杂简单。直接调用接口进行处理,对象会沿着树结构递归下去

识别方法: 组合模式会将同一抽象或接口类型的实例放入树状结构。

UML

在这里插入图片描述

实现方式

  1. 首先确保你自己 的模型能被拆分为树状结构。并且尝试分解为简单元素和容器(必须能够同时包含简单元素和其他容器)
  2. 声明组件接口及其一系列方法, 这些方法对简单和复杂元素都有意义。
  3. 创建一个叶节点类表示简单元素。 程序中可以有多个不同的叶节点类。
  4. 创建一个容器类表示复杂元素,创建一个数组成员变量来存储对于其子元素的引用。需要能同时保存叶节点和容器(父类)
  5. 在容器中定义添加和删除子元素的方法。

#include <algorithm>
#include <iostream>
#include <list>
#include <string>/*** 基础组件类声明了复合对象和简单对象的共同操作。*/
class Component
{/*** @var Component*/
protected:Component *parent_;/*** 可选地,基础组件可以声明一个接口,用于设置和访问组件在树结构中的父级。它还可以为这些方法提供一些默认实现。*/
public:virtual ~Component() {}void SetParent(Component *parent){this->parent_ = parent;}Component *GetParent() const{return this->parent_;}/*** 在某些情况下,在基础组件类中定义管理子组件的操作会很有益。这样,即使在对象树组装期间,您也不需要向客户端代码公开任何具体的组件类。缺点是对于叶级组件,这些方法将为空。*/virtual void Add(Component *component) {}virtual void Remove(Component *component) {}/*** 您可以提供一个方法,让客户端代码确定组件是否可以拥有子组件。*/virtual bool IsComposite() const{return false;}/*** 基础组件可以实现一些默认行为,或者将其委托给具体类(通过将包含行为的方法声明为“abstract”)。*/virtual std::string Operation() const = 0;
};/*** 叶级类表示组合的末端对象。叶级对象不能有任何子节点。** 通常情况下,真正的工作是由叶级对象完成的,而复合对象只是将任务委派给它们的子组件。*/
class Leaf : public Component
{
public:std::string Operation() const override{return "Leaf";}
};/*** Composite 类代表可能具有子组件的复杂组件。* 通常,Composite 对象将实际工作委托给它们的子组件,* 然后“汇总”结果。*/
class Composite : public Component
{
protected:std::list<Component *> children_;public:/*** Composite 对象可以向其子组件列表中添加或删除其他组件(简单或复杂)。*/void Add(Component *component) override{this->children_.push_back(component);component->SetParent(this);}/*** 注意,此方法只是从列表中移除指针,但不释放内存,* 您应该手动释放内存,或者最好使用智能指针。      * 我使用手动,简单理解*/void Remove(Component *component) override{children_.remove(component);component->SetParent(nullptr);delete component;}bool IsComposite() const override{return true;}/*** Composite 在特定方式下执行其主要逻辑。* 它递归遍历所有子组件,收集和汇总它们的结果。* 由于 Composite 的子组件会将这些调用传递给它们的子组件,以此类推,* 整个对象树将被遍历并返回结果。*/std::string Operation() const override{std::string result;for (const Component *c : children_){if (c == children_.back()){result += c->Operation();}else{result += c->Operation() + "+";}}return "Branch(" + result + ")";}
};/*** 客户端代码通过基础接口与所有组件一起工作。*/
void ClientCode(Component *component)
{// ...std::cout << "RESULT: " << component->Operation(); //同一接口// ...
}/*** 由于子组件管理操作在基类 Component 中声明,* 客户端代码可以处理任何组件,无论是简单还是复杂,而不依赖于具体的类。*/
void ClientCode2(Component *component1, Component *component2)
{// ...if (component1->IsComposite()){component1->Add(component2);}std::cout << "RESULT: " << component1->Operation();// ...
}/*** 这样,客户端代码可以支持简单的叶子组件...*/int main()
{Leaf *leaf = new Leaf;std::cout << "Client: 我有一个简单的组件:\n";ClientCode(leaf);std::cout << "\n\n";/*** ...以及复杂的 Composite 组件。*/Composite *tree = new Composite;Composite *branch1 = new Composite;Composite *branch2 = new Composite;Leaf *leaf1 = new Leaf;Leaf *leaf2 = new Leaf;Leaf *leaf3 = new Leaf;Leaf *leaf4 = new Leaf;branch1->Add(leaf1);branch1->Add(leaf2);branch2->Add(leaf3);branch2->Add(leaf4);tree->Add(branch1);tree->Add(branch2);std::cout << "Client: 现在我有一个复合树结构:\n";ClientCode(tree);std::cout << "\n\n";std::cout << "Client: 即使在管理树结构时,我不需要检查组件的具体类:\n";ClientCode2(tree, leaf);std::cout << "\n";delete leaf;delete tree;delete branch1;delete branch2;delete leaf1;delete leaf2;delete leaf3;delete leaf4;system("pause");return 0;
}

应用场景

  1. 实现树状对象结构, 可以使用组合模式。

大致可以理解为,两种共享公共接口的基本元素: 简单叶节点和复杂容器(包含其他容器)

  1. 客户端代码以相同方式处理简单和复杂元素

同一接口就是相同方式,但可以使用组合模式同时处理简单和复杂元素。

与其他模式的关系

  1. 桥接模式、 状态模式和策略模式 (在某种程度上包括适配器模式:将转换接口的工作给适配器) 模式的接口非常相似。 实际上, 它们都基于组合模式——即将工作委派给其他对象, 不过也各自解决了不同的问题。
    (模式并不只是以特定方式组织代码的配方, 你还可以使用它们来和其他开发者讨论模式所解决的问题。)

  2. 你可以在创建复杂组合树时使用生成器模式, 因为这可使其构造步骤以递归的方式(特定的方式)运行。

  3. 责任链模式通常和组合模式结合使用。 在这种情况下, 叶组件接收到请求后, 可以将请求沿包含全体父组件的链一直传递至对象树的底部。

  4. 你可以使用 迭代器模式 来遍历组合树。
    (对不同结构使用不同的迭代方式,有点类似策略模式:使用对应的算法解决问题)

  5. 你可以使用访问者模式对整个组合树执行操作。

  6. 你可以使用享元模式实现组合树的共享叶节点以节省内存。

  7. 组合和装饰模式的结构图很相似, 因为两者都依赖递归组合来组织无限数量的对象。

    • 装饰类似于组合,对一个产品套上一层层装饰,最终的结构十分相似。 但其只有一个子组件。 此外还有一个明显不同: 装饰为被封装对象添加了额外的职责, 组合仅对其子节点的结果进行了 “求和”。
    • 模式也可以相互合作: 你可以使用装饰来扩展组合树中特定对象的行为。
    • 大量使用组合和装饰的设计通常可从对于原型模式的使用中获益。 你可以通过该模式来复制复杂结构, 而非从零开始重新构造。

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

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

相关文章

蓝桥杯刷题day14——盖印章【算法赛】

一、问题描述 小 Z 喜欢盖印章。 有一天,小 Z 得到了一个 nm 的网格图,与此同时,他的手上有两种印章(分别称为 A,B),如下图所示。 他想将这两种印章盖在这个网格图上。 由于小 Z 是一个有原则的人,他将按照以下规则进行操作。 每个印章所形成的图案的边必须和网格图…

plasmo浏览器插件框架使用react和ant.design框架创建页面内容脚本UI样式注入

使用plasmo开发浏览器插件的时候&#xff0c;想要使用内容脚本UI注入自定义的UI组件&#xff0c;官方文档&#xff1a;Content Scripts UI – Plasmo&#xff0c;最好是搭配上好看的UI样式&#xff0c;所以可以集成ant.design的UI组件库&#xff0c;但是只集成组件还不行&#…

[C语言]——动态内存经典笔试题分析

目录 一.题目1 1.运行结果 2.分析 3.问题所在 4.更正 二.题目2 1.运行结果 2.分析 3.问题所在 4.更正 三.题目3 1.问题所在 2.更正&#xff1a; 四.题目4 一.题目1 void GetMemory(char *p){p (char *)malloc(100);}void Test(void){char *str NULL;GetMemory…

2024春算法训练4——函数与递归题解

一、前言 感觉这次的题目都很好&#xff0c;但是E题....&#xff08;我太菜了想不到&#xff09;&#xff0c;别人的题解都上百行了&#xff0c;晕&#xff1b; 二、题解 A-[NOIP2010]数字统计_2024春算法训练4——函数与递归 (nowcoder.com) 这种题目有两种做法&#xff1a;…

泛微OA 自定义多选浏览框

1、建模引擎-》应用建模-》表单 2、建模引擎-》应用建模-》模块 3、建模引擎-》应用建模-》查询 4、把查询页面挂到前端页面。 效果展示&#xff1a; 5、建模引擎-》应用建模-》浏览框 6、流程表单中字段应用

Windows Edge浏览器的兼容性问题及解决方案

1、Windows Edge&#xff08;了解 Microsoft Edge&#xff09;&#xff1a; 简单介绍&#xff1a; Microsoft Edge是一款由微软开发的网页浏览器&#xff0c;最初于2015年伴随Windows 10推出&#xff0c;作为Internet Explorer的继任者&#xff0c;旨在提供更快、更安全、更现代…

CV论文--2024.4.8

1、OW-VISCap: Open-World Video Instance Segmentation and Captioning 中文标题&#xff1a;OW-VISCap&#xff1a;开放世界视频实例分割和字幕 简介&#xff1a;开放世界视频实例分割是一项重要的视频理解任务&#xff0c;然而现有的方法存在一些限制。大多数方法要么只能在…

Springer旗下28年老刊,仅1个月录用,首个CCF推荐将被剔除?

毕业推荐 SSCI • 社科类&#xff0c;分区稳步上升&#xff08;最快13天录用&#xff09; IEEE&#xff1a; • 计算机类&#xff0c;1区(TOP)&#xff0c;CCF推荐 SCIE • 计算机工程类&#xff0c;CCF推荐&#xff08;最快16天录用&#xff09; 近日更新报道新增5本期刊…

Day79:服务攻防-中间件安全IISApacheTomcatNginx弱口令不安全配置CVE

目录 中间件-IIS-短文件&文件解析&蓝屏&写权限 HTTP.SYS&#xff08;CVE-2015-1635&#xff09;主要用作蓝屏破坏&#xff0c;跟权限不挂钩 IIS短文件(iis全版本都可能有这个问题) IIS文件解析 IIS写权限 中间件-Nginx-文件解析&目录穿越漏洞&CRLF …

Git 配置BCompare工具

一、Git配置BCompare工具 1、安装BCompare工具 下载BCompare安装包&#xff0c;打开安装包直接安装即可&#xff0c;如下&#xff1a; 2、禁止BCompare访问网络 网络进出站进行配置&#xff0c;限制BCompare访问网络&#xff0c;如果不进行上网限制&#xff0c;可能存在被封的…

Spring Security——08,自定义失败处理

自定义失败处理 一、自定义实现类1.1 实现AccessDeniedHandler1.2 实现AuthenticationEntryPoint 二、配置SpringSecurity三、测试3.1 认证失败3.2 权限不足 一键三连有没有捏~~ 我们还希望在认证失败或者是授权失败的情况下也能和我们的接口一样返回相同结构的json&#xff0c…

工业视觉检测

目录 我对工业视觉检测的了解 一、关键组成部分 二、应用场景 三、技术挑战 我对工业视觉检测的了解 工业视觉检测是利用机器视觉技术对产品质量进行自动化检查的过程&#xff0c;它在制造业中扮演着至关重要的角色&#xff0c;用于确保产品质量、提高生产效率、减少人工成…

Linux从入门到精通 --- 4(上).快捷键、软件安装、systemctl、软链接、日期和时区、IP地址

文章目录 第四章(上)&#xff1a;4.1 快捷键4.1.1 ctrl c 强制停止4.1.2 ctrl d 退出4.1.3 history4.1.4 历史命令搜索4.1.5 光速移动快捷键4.1.6 清屏 4.2 软件安装4.2.1 yum4.2.2 apt 4.3 systemctl4.4 软链接4.4.1 ln 4.5 日期和时区4.5.1 date命令4.5.2 date进行日期加减…

STC89C51学习笔记(五)

STC89C51学习笔记&#xff08;五&#xff09; 综述&#xff1a;文本讲述了代码中速写模板的创建、如何将矩阵键盘的按键与数字一一对应以及如何创建一个矩阵键盘密码锁。 一、速写模板 点击“templates”&#xff0c;再鼠标右键选择配置&#xff0c;按照以下方式即可修改一些…

倒反天罡的ssh后门 | Linux 后门系列

0x00 简介 今天看见有安全研究员发了一篇 ssh 后门的文章&#xff0c;复现思考后分享给大家 https://blog.thc.org/infecting-ssh-public-keys-with-backdoors 0x01 ssh密钥登录 参考 https://www.commandlinux.com/man-page/man5/authorized_keys.5.html 运维人员管理 Linux …

【Linux ARM 裸机】开发环境搭建

1、Ubuntu 和 Windows 文件互传 使用过程中&#xff0c;要频繁进行 Ubuntu 和 Windows 的文件互传&#xff0c;需要使用 FTP 服务&#xff1b; 1.1、开启 Ubuntu 下的 FTP 服务 //安装 FTP 服务 sudo apt-get install vsftpd //修改配置文件 sudo vi /etc/vsftpd.conf//重启…

rsync 远程同步----------安全高效的异地备份策略

目录 一、rsync介绍 rsync和cp的区别 rsync和scp的区别 二、rsync同步方式 rsync备份的方式 三、配置rsync源服务器 ①本地复制 ②下行同步 ③上行同步 四、常用Rsync命令 五、配置源的两种表达方法 六、部署rsync下行同步 ①环境准备 ②配置rsync源服务器-------…

【Leetcode每日一题】 动态规划 - LCR 166. 珠宝的最高价值(难度⭐⭐)(52)

1. 题目解析 题目链接&#xff1a;LCR 166. 珠宝的最高价值 这个问题的理解其实相当简单&#xff0c;只需看一下示例&#xff0c;基本就能明白其含义了 2.算法原理 想象一下&#xff0c;你正在玩一个寻宝游戏&#xff0c;游戏地图是一个二维网格&#xff0c;每个格子都藏有一…

单片机IGBT驱动电路一例

概述&#xff1a; 驱动的作用有三个&#xff1a; 1.是作为放大器获得所需要的驱动电压。 2.是提高输出电流能力。 3.是进行功率回路和控制回路的隔离 信号从MCU到IGBT驱动芯片 首先驱动电流需要放大 MCU的输出电流是mA级别&#xff0c;而IGBT需要的驱动电流可能达到几安培…

SpringCloud Alibaba Sentinel 简介和安装

一、前言 接下来是开展一系列的 SpringCloud 的学习之旅&#xff0c;从传统的模块之间调用&#xff0c;一步步的升级为 SpringCloud 模块之间的调用&#xff0c;此篇文章为第十三篇&#xff0c;即介绍 SpringCloud Alibaba Sentinel 简介和安装。 二、Sentinel 简介 2.1 Sent…