C++设计模式---组合模式

news2024/10/6 18:24:03

文章目录

  • 使用场景
  • 组合模式的定义
    • 安全组合模式


使用场景

组合模式和类与类之间的组合是不同的概念。

组合模式主要用来处理树形结构的数据,如果要表达的数据不是树形结构,就不太适合组合模式。

比如我们有一个目录结构:
在这里插入图片描述

这个目录我们把它绘制成树形结构:
在这里插入图片描述

如何利用程序把这个目录结构给绘制出来呢?

我们可以创建一个文件类,用来保存普通文件,然后再创建一个目录类,这个目录类中有一个list来保存子目录和子文件:

namespace hjl_project1
{	
	//文件相关类
	class File
	{
	public:
		//构造函数
		File(string name) :m_sname(name) {}

		//显示文件名
		void ShowName(string lvlstr) //lvlstr:为了显示层次关系的缩进字符串内容
		{
			cout << lvlstr << "-" << m_sname << endl; //显示“-”代表是一个文件,属末端节点(不会再有子节点)
		}

	private:
		string m_sname; //文件名
	};

	//目录相关类
	class Dir
	{
	public:
		Dir(string name) :m_sname(name) {}

	public:
		//目录中可以增加其他文件
		void AddFile(File* pfile)
		{
			m_childFile.push_back(pfile);
		}
		//目录中可以增加其他目录
		void AddDir(Dir* pdir)
		{
			m_childDir.push_back(pdir);
		}

		//显示目录名,同时也要负责其下面的文件和目录名的显示工作
		void ShowName(string lvlstr)//lvlstr:为了显示层次关系的缩进字符串内容
		{
			//(1)输出本目录名
			cout << lvlstr << "+" << m_sname << endl; //显示“+”代表是一个目录,其中会包含其他内容

			//(2)输出所包含的文件名
			lvlstr += "    "; //本目录中的文件和目录的显示,要缩进一些来显示
			for (auto iter = m_childFile.begin(); iter != m_childFile.end(); ++iter)
			{
				(*iter)->ShowName(lvlstr); //显示文件名
			}

			//(3)输出所包含的目录名
			for (auto iter = m_childDir.begin(); iter != m_childDir.end(); ++iter)
			{
				(*iter)->ShowName(lvlstr); //显示目录名,这里涉及了递归调用 
			}
		}

	private:
		string m_sname; //目录名
		list<File*> m_childFile; //目录中包含的文件列表
		list<Dir*> m_childDir; //目录中包含的子目录列表
	};

}

然后创建出目录和文件,在父目录下添加子目录和文件即可:

int main()
{
	using namespace hjl_project1;
	//(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(""); //缩进字符刚开始可以为空
	pdir2->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;
	
	return 0;
}

在这里插入图片描述

上面的例子存在一些问题,为了区分目录和文件我们引入了两种类,这种区分是比较多余的。

在组合模式中,不再将目录和文件两种类单独分开,而是引入新的抽象类提供公共接口,目录和文件两种类继承这个抽象类。

namespace hjl_project2
{
	//抽象父类FileSystem(抽象接口)
	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(string name) :m_sname(name) {}
		//显示名
		virtual void ShowName(int level)
		{
			for (int i = 0; i < level; ++i)
			{
				cout << "    "; //显示若干个空格用于对齐
			}
			cout << "-" << m_sname << endl;
		}
		virtual int Add(FileSystem* pfilesys)
		{
			return -1;
		}
		virtual int Remove(FileSystem* pfilesys)
		{
			return -1;
		}

	private:
		string m_sname; //文件名
	};

	//目录相关类
	class Dir :public FileSystem
	{
	public:
		//构造函数
		Dir(string name) :m_sname(name) {}

		//显示名字
		virtual void ShowName(int level)
		{
			//(1)显示若干个空格用于对齐
			for (int i = 0; i < level; ++i) { cout << "    "; }
			//(2)输出本目录名
			cout << "+" << m_sname << endl;
			//(3)显示的层级向下走一级
			level++;
			//(4)输出所包含的子内容(可能是文件,也可能是目录)
			//遍历目录中的文件和子目录
			for (auto iter = m_child.begin(); iter != m_child.end(); ++iter)
			{
				(*iter)->ShowName(level);
			}
		}

		virtual int Add(FileSystem* pfilesys)
		{
			m_child.push_back(pfilesys);
			return 0;
		}

		virtual int Remove(FileSystem* pfilesys)
		{
			m_child.remove(pfilesys);
			return 0;
		}

	private:
		string m_sname; //文件名
		list<FileSystem*> m_child; //目录中包含的文件或者其他目录列表
	};
}
int main()
{
	using namespace hjl_project2;

	//(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); //缩进字符刚开始可以为空
	//pdir2->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;

	return 0;
}

在这里插入图片描述

在这里插入图片描述


组合模式的定义

将一组对象(文件和目录)组织成属性结构以表示“部分整体”的层次结构(目录中包含文件和子目录),是的用户对蛋哥对象(文件)和组合对象(目录)的操作/使用/处理具有一致性(不用区分树叶和树枝,两者都有相同的接口)。

使用组合模式的前提是,具体的数据必须能够以树形结构的方式表示,数中包含了单个对象和组合对象。该模式专注于树形结构中蛋哥对象和组合对象的递归遍历。

主要有三种角色:

  1. 抽象组件:上面的filesystem类
  2. 叶子组件:file类
  3. 树枝组件:dir类,可以包含子节点,子节点可以是树枝也可以是树叶

组合模式的优点:

  1. 简化了代码的书写。
  2. 符合开闭原则,后续增加叶子组件或者树枝组件,只需要创建新类并继承抽象组件即可。
  3. 方便处理复杂的树形结构。

安全组合模式

上面的组合模式属于透明组合模式:在抽象组件中声明了所有用于管理和访问子节点的成员函数的实现手段。这也就意味着叶子组件和树枝组件都需要实现他们。

而安全组合模式:抽象组件中用于管理和访问子节点的成员函数被转移到了树枝组件中。

namespace hjl_project3
{
	class Dir;
	//抽象父类FileSystem(抽象接口)
	class FileSystem
	{
	public:
		virtual void ShowName(int level) = 0; //显示名字,参数level用于表示显示的层次,用于显示对齐

		virtual int countNumOfFiles() = 0; //统计目录下包含的文件个数
		
		virtual Dir* ifCompositeObj() { return nullptr; } //判断是否是一个树枝(组合对象)
		virtual ~FileSystem() {} //做父类时析构函数应该为虚函数
	};

	//文件相关类
	class File :public FileSystem
	{
	public:
		//构造函数
		File(string name) :m_sname(name) {}
		//显示名
		virtual void ShowName(int level)
		{
			for (int i = 0; i < level; ++i)
			{
				cout << "    "; //显示若干个空格用于对齐
			}
			cout << "-" << m_sname << endl;
		}		

		virtual int countNumOfFiles()//统计目录下包含的文件个数
		{
			return 1; //文件节点,做数量统计时按1计算
		}

	private:
		string m_sname; //文件名
	};

	//目录相关类
	class Dir :public FileSystem
	{
	public:
		//构造函数
		Dir(string name) :m_sname(name) {}

		//显示名字
		virtual void ShowName(int level)
		{
			//(1)显示若干个空格用于对齐
			for (int i = 0; i < level; ++i) { cout << "    "; }
			//(2)输出本目录名
			cout << "+" << m_sname << endl;
			//(3)显示的层级向下走一级
			level++;
			//(4)输出所包含的子内容(可能是文件,也可能是目录)
			//遍历目录中的文件和子目录
			for (auto iter = m_child.begin(); iter != m_child.end(); ++iter)
			{
				(*iter)->ShowName(level);
			}
		}

		int Add(FileSystem* pfilesys)
		{
			m_child.push_back(pfilesys);
			return 0;
		}

		int Remove(FileSystem* pfilesys)
		{
			m_child.remove(pfilesys);
			return 0;
		}

		virtual Dir* ifCompositeObj() { return this; }

		virtual int countNumOfFiles()//统计目录下包含的文件个数
		{
			int iNumOfFiles = 0;
			for (auto iter = m_child.begin(); iter != m_child.end(); ++iter)
			{
				iNumOfFiles += (*iter)->countNumOfFiles(); //递归调用
			}
			return iNumOfFiles;
		}

	private:
		string m_sname; //文件名
		list<FileSystem*> m_child; //目录中包含的文件或者其他目录列表
	};

}


int main()
{
	using namespace hjl_project3;

	//(1)创建各种目录,文件对象
	Dir* pdir1 = new Dir("root");
	//----
	FileSystem* pfile1 = new  File("common.mk");
	FileSystem* pfile2 = new  File("config.mk");
	FileSystem* pfile3 = new  File("makefile");
	//-----
	Dir* pdir2 = new Dir("app");
	FileSystem* pfile4 = new  File("nginx.c");
	FileSystem* pfile5 = new  File("ngx_conf.c");
	//-----
	Dir* pdir3 = new Dir("signal");
	FileSystem* pfile6 = new  File("ngx_signal.c");
	//-----
	Dir* 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); //缩进字符刚开始可以为空
	//pdir2->ShowName("");

	cout<<"文件个数:"<<pdir1->countNumOfFiles()<<endl;

	//(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;

	return 0;
}

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

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

相关文章

图解LeetCode——1704. 判断字符串的两半是否相似(难度:简单)

一、题目 给你一个偶数长度的字符串 s 。将其拆分成长度相同的两半&#xff0c;前一半为 a &#xff0c;后一半为 b 。 两个字符串 相似 的前提是它们都含有相同数目的元音&#xff08;a&#xff0c;e&#xff0c;i&#xff0c;o&#xff0c;u&#xff0c;A&#xff0c;E&…

几分钟实现对恶意IP地址进行拦截,腾讯云Web防火墙实在太香了!

一、概述 在平时上网中&#xff0c;我们经常听到“xxx被拉入黑名单”、“把xxx加入白名单”&#xff0c;黑白名单成了禁止访问和允许访问的代名词&#xff0c;黑白名单是一种常见的安全机制&#xff0c;用于隔离流量&#xff0c;然后对隔离的流量采取特定操作。 黑名单代表只…

Redis高可用之持久化

一 Redis高可用 什么是高可用 在web服务器中&#xff0c;高可用是指服务器可以正常访问的时间&#xff0c;衡量的标准是在多长时间内可以提供正常服务(99.9%、99.99%、99.999%等等)。 但是在Redis语境中&#xff0c;高可用的含义似乎要宽泛一些&#xff0c;除了保证提供正常…

Pytorch实战:基于鲸鱼WOA优化1DCNN的轴承故障诊断

目录 0.引言 1.关键点 2.WOA优化1DCNN超参数实战 2.1 数据准备 2.2 1DCNN故障诊断建模 2.3 采用WOA优化1DCNN超参数 0.引言 采用1DCNN进行轴承故障诊断建模&#xff0c;并基于鲸鱼优化算法WOA对1DCNN的超参数进行优化&#xff0c;以实现更高的精度。建立一个两层的1DCNN&a…

【Transformers】第 9 章 :处理很少或没有标签

&#x1f50e;大家好&#xff0c;我是Sonhhxg_柒&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流&#x1f50e; &#x1f4dd;个人主页&#xff0d;Sonhhxg_柒的博客_CSDN博客 &#x1f4c3; &#x1f381;欢迎各位→点赞…

[C]实现能在本地存储的简易通讯录

作者&#xff1a; 华丞臧. 专栏&#xff1a;【C语言】 各位读者老爷如果觉得博主写的不错&#xff0c;请诸位多多支持(点赞收藏关注)。如果有错误的地方&#xff0c;欢迎在评论区指出。 文章目录一、文件动态通讯录二、相关代码解析2.1 初始化2.2 销毁通讯录2.3 增加联系人2.4 …

NLP模型(一)——word2vec实现

文章目录1. 整体思路2. 数据处理3. 数据准备4. 创建数据管道5. 构建模型6. 模型训练7. 加载模型得到词向量8. 总结前面我介绍了word2vec算法的两种实现算法&#xff0c;Skip−gramSkip-gramSkip−gram 以及 CBOWCBOWCBOW 算法&#xff0c;我认为理解一个算法最好的方法就是复现…

stft的窗函数设计要求和方法(COLA)

在语音处理进行短时傅里叶变换的时候&#xff0c;对窗函数是有一定要求的&#xff0c;这篇文章将对这方面的问题进行简单的阐述。 一、背景描述 常用的语音处理需要进行这样处理: stft分帧会对信号产生截断&#xff0c;为尽可能避免这种影响&#xff0c;应考虑考虑加合适的窗 …

互融云借条APP系统开发 六大系统优势全面保障

借条是指借个人或公家的现金或物品时写给对方的条子。它是一种凭证性文书&#xff0c;通常用于日常生活以及商业管理方面。借条的本质就是借款合同&#xff0c;只不过形式比较简单&#xff0c;那么电子借条也就是简单的电子借款合同。与传统的纸质合同相比&#xff0c;电子借条…

目标检测算法——YOLOv5/YOLOv7改进之结合无参注意力SimAM(涨点神器)

目录 &#xff08;一&#xff09;前言介绍 1.摘要 2.不同注意力步骤比较 &#xff08;二&#xff09;相关实验 &#xff08;三&#xff09;YOLOv5结合无参注意力SimAM 1.配置.yaml文件 2.配置common.py 3.修改yolo.py SimAM&#xff1a;无参数Attention助力分类/检测/分…

想带着学生做一个操作系统,可行性有多大?

有知乎网友提问如下: 想带着学生做一个操作系统&#xff0c;可行性有多大&#xff1f; 个人觉得可行性非常大&#xff0c;如果只是做着来玩&#xff0c;让学生了解操作系统时如何实现的话。但是&#xff0c;如果你打算今后商业化的话&#xff0c;那就另当别论了。就算你能做出来…

单片机实验——水塔自动抽水系统设计(基于Proteus仿真)

实验内容及要求 自来水供水是现代生活的一大特点&#xff0c;水塔作为储水装置是自来水系统必不可少的重要设施&#xff0c;让水塔保持一定的水量是自来水不断供的必要条件&#xff0c;本设计模拟自来水系统中水塔的自动抽水机制&#xff0c;设计分为控制系统和虚拟水塔两部分…

若依管理框架-漏洞复现

文章目录 0x00 介绍0x01 默认口令漏洞0x02 SQL注入0x03 Shiro反序列化漏洞0x04 任意文件读取/下载0x05 定时任务0x06 `swagger-ui.html`接口文档泄漏0x07 Druid未授权访问摘抄免责声明0x00 介绍 RuoYi 是一个 Java EE 企业级快速开发平台,基于经典技术组合(Spring Boot、Apa…

黑产工具情报的分析方式浅析

接下来我们以恶意爬虫、抢券工具和注册机三种工具来谈一下黑产工具情报的分析方式。 对于企业方面来说&#xff0c;黑产工具情报可以有效的提高业务安全的攻防效率。通过分析工具利用的业务接口&#xff0c;不仅可以将黑产作恶行为进行有效的追踪&#xff0c;对其进行有效的处…

Java本地搭建宝塔部署实战springboot自动化立体智慧仓库WMS源码

大家好啊&#xff0c;我是测评君&#xff0c;欢迎来到web测评。 本期给大家带来一套Java开发的springboot自动化立体智慧仓库WMS源码。 技术架构 技术框架&#xff1a;SpringBoot layui HTML CSS JS运行环境&#xff1a;jdk8 IntelliJ IDEA maven3 宝塔面板 本地搭建教…

MongoDB入门与实战-第四章-SpringBoot集成MongoDB

目录参考MongoDB 连接java客户端方式引入驱动依赖测试创建客户端创建集合查询文档查询集合大小条件查询排序投影聚合查询复合聚合插入文档批量插入更新文档删除文档SpringDataMongoDB添加依赖配置文件新建实体映射插入文档修改文档删除参考 SpringBoot 整合 MongoDB 实战解说 …

[附源码]java毕业设计濒危物种科普系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

jq获取和设置偏移量:offset()、position()

jq获取和设置偏移量&#xff1a; js获取盒子的偏移量是&#xff1a;offsetLeft、offsetTop&#xff1b;jq获取盒子的偏移量的方法&#xff1a;offset&#xff08;&#xff09;、position&#xff08;&#xff09;&#xff1b;offset&#xff08;&#xff09;&#xff1a;距离文…

去除有重复的行

【问题】 I have a csv file and I have duplicate as well as unique data getting add to it on a daily basis. This involves too many duplicates. I have to remove the duplicates based on specific columns. For eg: csvfile1: title1 title2 title3 title4 title5…

C++程序开启大地址(虚拟内存),让32位程序使用4G内存的方,虚拟内存概念及寻址范围详解

如何让 32 位程序突破 2G 内存限制 一般情况下&#xff0c;32 位程序的内存大小被限制在了 2G&#xff0c;不过可以通过以下的操作来突破这个限制。 修改操作系统参数 这一步骤只针对 32 位操作系统&#xff0c;64 位操作系统可以跳过 用管理员权限打开一个命令行窗口 执行…