C++设计模式---组合模式
创始人
2024-04-07 00:01:01
0

文章目录

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


使用场景

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

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

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

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

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

我们可以创建一个文件类,用来保存普通文件,然后再创建一个目录类,这个目录类中有一个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 m_childFile; //目录中包含的文件列表list 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 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 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<<"文件个数:"<countNumOfFiles()<

相关内容

热门资讯

银河麒麟V10SP1高级服务器... 银河麒麟高级服务器操作系统简介: 银河麒麟高级服务器操作系统V10是针对企业级关键业务...
【NI Multisim 14...   目录 序言 一、工具栏 🍊1.“标准”工具栏 🍊 2.视图工具...
AWSECS:访问外部网络时出... 如果您在AWS ECS中部署了应用程序,并且该应用程序需要访问外部网络,但是无法正常访问,可能是因为...
不能访问光猫的的管理页面 光猫是现代家庭宽带网络的重要组成部分,它可以提供高速稳定的网络连接。但是,有时候我们会遇到不能访问光...
AWSElasticBeans... 在Dockerfile中手动配置nginx反向代理。例如,在Dockerfile中添加以下代码:FR...
Android|无法访问或保存... 这个问题可能是由于权限设置不正确导致的。您需要在应用程序清单文件中添加以下代码来请求适当的权限:此外...
月入8000+的steam搬砖... 大家好,我是阿阳 今天要给大家介绍的是 steam 游戏搬砖项目,目前...
​ToDesk 远程工具安装及... 目录 前言 ToDesk 优势 ToDesk 下载安装 ToDesk 功能展示 文件传输 设备链接 ...
北信源内网安全管理卸载 北信源内网安全管理是一款网络安全管理软件,主要用于保护内网安全。在日常使用过程中,卸载该软件是一种常...
AWS管理控制台菜单和权限 要在AWS管理控制台中创建菜单和权限,您可以使用AWS Identity and Access Ma...