设计模式学习[17]---组合模式

文章目录

  • 前言
    • 1.引例
    • 2.一致性抽象处理
    • 3.透明组合模式与安全组合模式
  • 总结

前言

在画类图的时候,类与类之间有组合关系,聚合关系,我本来以为这个组合模式应该是整体与部分的关系,其实设计模式中的组合模式和类图中的组合不是同一个东西。设计模式中的组合模式更像是一个行为统一且存在结构性的关系,例如树形结构。

1.引例

先看下面一个例子,在资源管理器中的文件目录中有这样的一个文件结构,root下面有common.mk,config.mk,makefile三个文件,以及app,signal,_include三个子文件夹,且子文件夹下还有对应的文件。
左侧是结构图,右侧是树形图。
在这里插入图片描述在这里插入图片描述

现在我们用一段代码来实现

#include <iostream>
#include <string>
#include <list>class File
{
public:File(std::string name) :m_sname(name) {}//显示文件名void ShowName(std::string lvlstr){std::cout<<lvlstr << "-" << m_sname << std::endl;}private:std::string m_sname;	//文件名
};class Dir
{
public:Dir(std::string name) :m_sname(name) {};void AddFile(File* pfile){m_childFile.push_back(pfile);}void AddDir(Dir* pDir){m_childDir.push_back(pDir);}void ShowName(std::string lvlstr){//输出本目录名std::cout << lvlstr << "+" << m_sname << std::endl;//输出所包含的文件名lvlstr += "  ";for (auto iter=m_childFile.begin();iter!=m_childFile.end();++iter){(*iter)->ShowName(lvlstr);}//输出所包含的目录名for (auto iter = m_childDir.begin(); iter != m_childDir.end(); ++iter){(*iter)->ShowName(lvlstr);}}
private:std::string m_sname;std::list<File*>m_childFile;std::list<Dir*>m_childDir;
};
int main()
{//(1)创建各种目录,文件对象Dir* pdir1 = new Dir("root");//-----File* pfile1 = new File("common.mk");File* pfile2 = new File("config.mk");File* pfile3 = new File("makefile");//-----Dir* pdir2 = new Dir("app");File* pfile4 = new File("nginx.c");File* pfile5 = new File("ngx_conf.c");//-----Dir * pdir3 = new Dir("signal");File* pfile6 = new File("ngx_signal.c");//-------Dir * pdir4 = new Dir(" include");File* pfile7 = new File("ngx_func.h");File* pfile8 = new File("ngx_signal.h");//(2)构造树形目录结构pdir1->AddFile(pfile1);pdir1->AddFile(pfile2);pdir1->AddFile(pfile3);//-----pdir1->AddDir(pdir2);pdir2->AddFile(pfile4);pdir2->AddFile(pfile5);pdir1->AddDir(pdir3);pdir3->AddFile(pfile6); pdir1->AddDir(pdir4);pdir4->AddFile(pfile7); pdir4->AddFile(pfile8);//(3)输出整个目录结构,只要调用根目录的ShowName方法即可,//每个目录都有自己的ShowName方法负责自己的文件和目录的显示pdir1->ShowName("");//(4)释放资源delete pfile8;delete pfile7;delete pdir4;//-------delete pfile6;delete pdir3;//-------delete pfile5;delete pfile4;delete pdir2;//------delete pfile3;delete pfile2;delete pfile1;delete pdir1;
}

在上述代码中,我们通过ShowName这个函数用来显示当前文件/目录的名称,这种显示名称的行为是否具有一致性?
对于一开始提及的需求,打印出层级结构信息,我们能否将一致性的操作提取出来呢?

如果不提取出来,那就是像上面代码一样,定义了两个类,这两个类之间的成员函数其实没有任何关系,虽然都是叫做ShowName,但分属于不同的类,如果共有的行为再多一点呢?比如输出类型,ShowType这种,其实行为也是一致的。

由此引出我们的组合模式的原理阐述:

组合模式
将一组对象(文件和目录)组织成树形结构以表示“部分-整体”的层次结构(目录中包含文件和子目录)。使得用户对单个对象(文件)和组合对象(日录)的操作/使用/处理(递归遍历并执行ShowName逻辑等)具有一致性。

在这个例子里面,File类型就是部分,Dir类型就是整体的层次结构。

2.一致性抽象处理

对于行为一致的情况,在设计模式中通常用接口进行抽象,我们再引入接口类FileSystem

#include <iostream>
#include <string>
#include <list>class FileSystem
{
public:virtual void ShowName(int level) = 0;//显示名字,用level表示显示的层级,用于显示对齐virtual int Add(FileSystem* pfilesys) = 0;virtual int Remove(FileSystem* pfilesys) = 0;virtual ~FileSystem() {}
};
class File:public FileSystem
{
public:File(std::string name) :m_sname(name) {}void ShowName(int level) override{for (int i = 0; i < level; ++i) { std::cout << "  "; };std::cout << "-" << m_sname << std::endl;}int Add(FileSystem* pfilesys)override{return -1;};int Remove(FileSystem* pfilesys)override{return -1;};private:std::string m_sname;	//文件名
};class Dir :public FileSystem
{
public:Dir(std::string name) :m_sname(name) {};int Add(FileSystem* pfilesys)override{m_child.push_back(pfilesys);return 0;}int Remove(FileSystem* pDir)override{m_child.remove(pDir);return 0;}void ShowName(int level)override{for (int i = 0; i < level; ++i) { std::cout << "  "; };std::cout << "+" << m_sname << std::endl;level++;for (auto iter=m_child.begin();iter!=m_child.end();++iter){(*iter)->ShowName(level);}}
private:std::string m_sname;std::list<FileSystem*>m_child;
};
int main()
{//(1)创建各种目录,文件对象FileSystem* pdir1 = new Dir("root");//-----FileSystem* pfile1 = new File("common.mk");FileSystem* pfile2 = new File("config.mk");FileSystem* pfile3 = new File("makefile");//-----FileSystem* pdir2 = new Dir("app");FileSystem* pfile4 = new File("nginx.c");FileSystem* pfile5 = new File("ngx_conf.c");//-----FileSystem * pdir3 = new Dir("signal");FileSystem* pfile6 = new File("ngx_signal.c");//-------FileSystem * pdir4 = new Dir(" include");FileSystem* pfile7 = new File("ngx_func.h");FileSystem* pfile8 = new File("ngx_signal.h");//(2)构造树形目录结构pdir1->Add(pfile1);pdir1->Add(pfile2);pdir1->Add(pfile3);//-----pdir1->Add(pdir2);pdir2->Add(pfile4);pdir2->Add(pfile5);pdir1->Add(pdir3);pdir3->Add(pfile6);pdir1->Add(pdir4);pdir4->Add(pfile7);pdir4->Add(pfile8);//(3)输出整个目录结构,只要调用根目录的ShowName方法即可,//每个目录都有自己的ShowName方法负责自己的文件和目录的显示pdir1->ShowName(0);//(4)释放资源delete pfile8;delete pfile7;delete pdir4;//-------delete pfile6;delete pdir3;//-------delete pfile5;delete pfile4;delete pdir2;//------delete pfile3;delete pfile2;delete pfile1;delete pdir1;
}

在这里插入图片描述

在上述定义中,用户是指main主函数中的调用代码,一致性指不用区分树叶还是树枝,两者都继承自FileSystem,都具有相同的接口,可以做相同的调用。可以看到,组合模式的设计思路其实就是用树形结构来组织数据,然后通过在树中递归遍历各个节点并在每个节点上统一地调用某个接口(例如,都调用ShowName成员函数)或进行某些运算。总之,组合模式之所以称为结构型模式,是因为该模式提供了一个结构,可以同时包容单个对象和组合对象。组合模式发挥作用的前提是具体数据必须能以树形结构的方式表示,树中包含了单个对象和组合对象。该模式专注于树形结构中单个对象和组合对象的递归遍历(只有递归遍历才能体现出组合模式的价值),能把相同的操作(FileSystem 定义的接口)应用在单个以及组合对象上,并且可以忽略单个对象和组合对象之间的差别。

3.透明组合模式与安全组合模式

这里我们将一致性的操作提取出来,作为一个接口,这样满足了用户对单个对象和组合对象的使用具有一致性。可是又引入了新的问题,我的文件File类型,其实不支持Add和Remove接口,没必要支持,因为它已经是一棵树中的叶子节点了,不能再增加和删除节点。这种实现方式在组合模式中称之为透明组合模式

它的优点就是保证所有的组件,都有相同的接口,确保了操作的一致性。但是缺点也很明显,存在部分组件需要实现本来不需要的接口,这就导致了冗余,同时也会造成偶发性的错误调用,比如错误的让File类的对象调用了Add函数,但是返回一个-1,并没有任何用处。

针对这个缺陷,我们引入安全组合模式
在这里插入图片描述
通过类图,我们可以看到,我们把共有的一致性操作还是抽象出来了,但是对于只有Dir这种独有的Add、Remove等操作,我们放到Dir自身上去定义与实现。

其实我在看书的时候就在想,既然是操作的一致性,为什么还要把Dir的一些接口比如AddFile抽象成Add,再放到FileSystem里面去,明明File没有Add这个操作。但是看了后面的透明组合模式和安全组合模式,我也不得不说,为了引出两个组合模式的区别吧。

最后贴一些组合模式的常用场景
(1)在公司组织结构中采用组合模式,公司组织架构如图
在这里插入图片描述

(2)对某个目录下的所有文件(包括子目录的文件)进行杀毒工作
在这里插入图片描述
(3)利用图元进行图形的绘制工作
在这里插入图片描述

总结

组合模式说的直白一点,两句话。

用在树形结构,存在部分与整体关系中。抽象出整体与部分的共有操作,用统一的接口实现

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

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

相关文章

48Days-Day12 | 添加字符,数组变换,装箱问题

添加字符 添加字符_牛客笔试题_牛客网 算法原理 因为本题数据量都比较小&#xff0c;所以我们可以直接使用暴力解法&#xff0c;枚举B字符串的每一个位置作为与A字符串比较的起点&#xff0c;维护一个最小位数的值 代码 import java.util.*;// 注意类名必须为 Main, 不要有…

关于npm前端项目编译时栈溢出 Maximum call stack size exceeded的处理方案

背景&#xff1a;使用vueelementui的前端项目&#xff0c;使用jenkins进行自动化编译部署&#xff0c;某天在进行编译发版的时候&#xff0c;突然出现 npm ERR! Maximum call stack size exceeded 错误&#xff0c;一直都没法编译成功。原因&#xff1a;随着前端项目的不断迭代…

微信小程序组件发布为 npm 包的具体步骤

1. 准备工作 首先&#xff0c;您需要在系统上安装 Node.js 和 npm。如果尚未安装&#xff0c;请访问 Node.js — Run JavaScript Everywhere 下载并安装最新版本。 2. 创建独立的组件目录 为了更好地管理组件&#xff0c;建议将其从当前项目中独立出来&#xff1a; wechat-…

LCM中间件入门(2):LCM核心实现原理解析

文章目录一、good()函数&#xff1a;LCM实例状态检查的实现原理1. 实现逻辑2. 简化代码示例&#xff08;C语言核心逻辑&#xff09;二、publish()&#xff1a;向指定channel发送消息的原理1. 完整流程拆解2. 简化代码示例&#xff08;C核心逻辑&#xff09;三、subscribe()&…

Nginx安装及配置

一.nginx安装1.1nginx概述1.1.1 nginx介绍Nginx是一款高性能的开源HTTP和反向代理服务器&#xff0c;是免费的、开源的、高性能的HTTP和反向代理服务器、邮件代理服务器、以及TCP/UDP代理服务器解决C10K问题&#xff08;10K Connections&#xff09;。同时也支持IMAP/POP3代理服…

SelectDB数据库,新一代实时数据仓库的全面解析与应用

摘要&#xff1a;SelectDB是一款基于Apache Doris的新一代实时数据仓库解决方案&#xff0c;具备实时极速、融合统一、弹性架构和开放生态四大核心特性。它采用云原生存算分离架构&#xff0c;支持秒级数据更新、毫秒级查询响应&#xff0c;在TPC-H等基准测试中性能超越传统系统…

自动驾驶的未来:多模态传感器钻机

伦敦大学学院博士生袁方正在建造多模态传感器钻机&#xff0c;以探索自动驾驶的未来。他的最新设置汇集了一套尖端传感器&#xff1a; &#x1f4e1; 60 GHz 雷达&#xff08;用于 Raspberry Pi 的 DreamHAT&#xff09;DreamRF &#x1f4f7; RGB 深度摄像头 &#xff08;Real…

13.Redis 的级联复制

Redis 的级联复制 即实现基于Slave节点的Slave 1. 修改 Slave 节点配置文件 # 第一个slave节点 [rootubuntu2204 ~]#vim /apps/redis/etc/redis.conf(大约在533行附近) replicaof 10.0.0.100 6379 masterauth 123456# 第二个slave节点 [rootubuntu2204 ~]#vim /apps/redis/etc/…

spring-ai-alibaba 学习(二十)——graph之检查点

前面学习了graph的基本概念&#xff0c;参数设置&#xff0c;特殊节点和边&#xff0c;今天学习一下检查点检查点可能名称比较抽象&#xff0c;换个名字可能比较容易理解&#xff0c;进度保存点或者存档点&#xff0c;可以类比游戏中保存当前游戏进度的存档进度主要用于人工介入…

sqli-labs:Less-19关卡详细解析

1. 思路&#x1f680; 本关的SQL语句为&#xff1a; $insert"INSERT INTO security.referers (referer, ip_address) VALUES ($uagent, $IP)";注入类型&#xff1a;字符串型&#xff08;单引号包裹&#xff09;、INSERT操作提示&#xff1a;参数需以闭合关键参数&a…

Java小红书源码1:1还原uniapp_仿小红书源码

在内容驱动型社交平台兴起的背景下&#xff0c;小红书作为图文/视频种草社区的代表&#xff0c;其产品结构与功能体验逐渐成为众多开发者与创业团队的模仿蓝本。本项目基于Java后端uni-app前端栈&#xff0c;完整复刻小红书主要功能&#xff0c;支持多端&#xff08;小程序、H5…

USB Type-C PD协议一文通

原文&#xff1a;https://www.richtek.com/Design%20Support/Technical%20Document/AN056?sc_langzh-TW译者&#xff1a;TrustZone1、概述 USB Type-C标准的出现是为了满足不断增长的现代设备之间的连接需要&#xff0c;它在传统USB标准的基础上提供了更高的电源传输能力和资料…

AI文档比对和Word的“比较”功能有什么区别?

AI文档比对工具的核心区别在于&#xff0c;它超越了Word的纯文本“找不同”&#xff0c;能精准处理扫描件、表格及印章&#xff0c;并将文档审查从被动的文本核对&#xff0c;处理大文档也更为快速及准确。 为什么Word的“比较”功能已经不够用了&#xff1f; 对于许多专业人士…

AI驱动SEO关键词智能进化

内容概要 随着人工智能&#xff08;AI&#xff09;技术的快速演进&#xff0c;搜索引擎优化&#xff08;SEO&#xff09;领域正迎来前所未有的变革。本文核心探讨AI如何驱动SEO关键词的智能进化&#xff0c;重点解析人工智能革新关键词研究与优化策略的机制&#xff0c;包括智能…

基于SpringBoot+MyBatis+MySQL+VUE实现的青年公寓服务平台管理系统(附源码+数据库+毕业论文+部署教程+配套软件)

摘 要 传统信息的管理大部分依赖于管理人员的手工登记与管理&#xff0c;然而&#xff0c;随着近些年信息技术的迅猛发展&#xff0c;让许多比较老套的信息管理模式进行了更新迭代&#xff0c;房屋信息因为其管理内容繁杂&#xff0c;管理数量繁多导致手工进行处理不能满足广…

12.Redis 主从复制

Redis 主从复制Redis 主从复制1. Redis 主从复制架构2. 主从复制实现2.1 主从命令配置2.1.1 启用主从同步2.1.2 查看日志观察同步状态2.1.3 修改 Slave 节点配置文件2.1.4 删除主从同步3. 主从复制故障恢复3.1 Slave 节点故障和恢复3.2 Master 节点故障和恢复3.3 常见主从复制故…

微服务的编程测评系统8-题库管理-竞赛管理

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录前言1. 添加题目1.1 service方法1.2 画页面-引入富文本和代码编辑框1.3 子组件中发送请求2. 获取题目详情3. 编辑题目4. 删除题目5. Vue生命周期函数5.1 创建阶段5.2…

基于springboot的学习辅导系统设计与实现

学生&#xff1a;注册登录&#xff0c;学习视频&#xff0c;学习资料&#xff0c;在线交流&#xff0c;系统公告&#xff0c;个人中心&#xff0c;后台管理教师&#xff1a;登录&#xff0c;个人中心&#xff0c;学习视频管理&#xff0c;学习资料管理&#xff0c;签到记录管理…

Kubernetes (K8s) 部署Doris

官网提供yaml地址下载部署 https://doris.apache.org/zh-CN/docs/2.0/install/cluster-deployment/k8s-deploy/install-env/禁用和关闭 swap 在部署 Doris 时&#xff0c;建议关闭 swap 分区。 通过以下命令可以永久关闭 swap 分区。 echo "vm.swappiness 0">>…

AI生成图片工具分享!

CZL在线工具箱近日推出了一款基于Cloudflare Workers AI的免费在线AI图片生成服务。该服务采用**Stable Diffusion XL&#xff08;SDXL&#xff09;**模型&#xff0c;为用户提供高质量、逼真的图像生成体验。 核心特性 全球GPU网络&#xff1a;基于Cloudflare全球分布式GPU网…