C++设计模式-组合模式 原创 设计模式 2021年10月22日 16:00 夏至未至 1132 当前内容 14838 字,在路上,马上到,马上到 ### 目录 [TOC] ### 组合模式介绍 #### 何为组合模式 组合模式主要是用来处理树形结构数据,组合多个对象形成树形结构以表示具有“整体—部分”关系的层次结构,可以简单理解为一组对象集合,其应用场景的特殊性,数据必须能表示成树形结构,组合模式对单个对象(即叶子对象)和组合对象(即容器对象)的使用具有一致性,组合模式又可以称为“整体—部分”(Part-Whole)模式,它是一种对象结构型模式。 #### 组合模式场景 例如操作系统中的目录结构、应用软件中的菜单、办公系统中的公司组织结构等等 #### 组合模式特点 叶子节点不再包含成员对象,而容器节点可以包含成员对象,这些对象可以是叶子节点,也可以是容器节点。 #### 为何使用组合模式 组合模式为处理树形结构提供了一种较为完美的解决方案,它描述了如何将容器和叶子进行递归组合,使得用户在使用时无须对它们进行区分,可以一致地对待容器和叶子。 #### 如何使用组合模式 树型结构的问题中,模糊了简单元素(叶子节点)和复杂元素的概念(容器节),为叶子节点和容器节点提供了公共的抽象构建类,客户端无需关心所操作的对象是叶子节点还是容器节点,只需针对抽象构建类编程处理即可。 ### 组合模式关键 组合模式的关键是定义一个公共的抽象构件类,它既可以代表叶子,又可以代表容器,而客户端针对该抽象构件类进行编程,无须知道它到底表示的是叶子还是容器,可以对其进行统一处理。叶子节点不再包含成员对象,而容器节点可以包含成员对象,这些对象可以是叶子节点,也可以是容器节点。 ### 应用场景 在具有整体和部分的层次结构中,希望通过一种方式忽略整体与部分的差异,客户端可以一致地对待它们。 ### 模式特点 #### 优点 1. 组合模式清楚地定义分层次的复杂对象,表示对象的全部或部分层次结构,让客户端忽略层次的差异; 2. 客户端可以一致地对待层次结构中各个层次的对象,而不必关心其具体对象,简化了客户端代码; 3. 在组合模式中增加新的叶子构件和容器构件非常方便,易于扩展,符合开闭原则; 4. 组合模式为树形结构的面向对象实现提供了一种灵活的解决方案,通过叶子对象和容器对象的递归组合。 #### 缺点 组合模式忽略了层次的差异,很难对容器中的构件类型进行限制,如果希望一个容器中只能有某些特定类型的对象,就必须通过在运行时进行类型检查来处理,这个实现过程较为复杂。 ### 模式角色 - Component(抽象构件):可以是接口或抽象类,定义了构件的一些公共接口,这些接口是管理或者访问它的子构件的方法(如果有子构件),具体的实现在叶子构件和容器构件中进行。 - Leaf(叶子构件):它代表树形结构中的叶子节点对象,叶子节点没有子节点,它实现了在抽象构件中定义的行为。对于那些访问及管理子构件的方法(非叶子构件功能,属于容器构件的功能),可以通过抛出异常、提示错误等方式进行处理。 - Composite(容器构件) :表示容器节点对象,容器节点包含子节点,其子节点可以是叶子节点,也可以是容器节点,它提供一个集合用于存储子节点,实现了在抽象构件中定义的行为。 ### 组合模式分类 根据抽象构件类的定义形式,可将组合模式分为透明组合模式和安全组合模式两种形式。可以简单地看抽象构件类包含的是叶子构件行为和容器构件行为的并集还是交集。 #### 透明组合模式 透明组合模式中,抽象构件类包含叶子构件行为和容器构件行为的并集,抽象构件Component中声明了所有用于管理成员对象的方法,包括add()、remove()以及rename()等方法,这样做的好处是确保所有的构件类都有相同的接口。透明组合模式也是组合模式的标准形式,管理成员对象的方法在叶子构件可见所以是透明的,对于这些方法需要通过抛出异常、提示错误等方式进行处理。 #### 安全组合模式 安全组合模式中,抽象构件类包含叶子构件行为和容器构件行为的交集,在抽象构件Component中没有声明任何用于管理成员对象的方法,而是在Composite类中声明并实现这些方法。这种做法是安全的,因为根本不向叶子对象提供这些管理成员对象的方法(比如新增,删除等高危操作),对于叶子对象,客户端不可能调用到这些方法。 ### 上机实操 #### 代码实现场景 对不对类型文件文件重命名,对文件夹分增删文件以及重命名 1. Component(抽象构件): AbstractFile 2. Leaf(叶子构件):ImageFile TextFile VideoFile 3. Composite(容器构件):Folder #### 透明组合模式 ##### 代码 #include #include // 透明组合模式 // Component(抽象构件): AbstractFile class AbstractFile { public: virtual ~AbstractFile() = default; // 抽象构件类包含叶子构件行为和容器构件行为的并集 // 确保所有的构件类都有相同的接口 // 新增接口 virtual void add(AbstractFile* file) = 0; // 删除接口 virtual void remove(AbstractFile* file) = 0; // 重命名接口 virtual void rename() = 0; protected: AbstractFile() = default; }; // Leaf(叶子构件),没有子节点 // ImageFile 图片文件 // TextFile 文本文件 // VideoFile 视频文件 class ImageFile : public AbstractFile { public: ImageFile(const std::string& name) { std::cout << "图片文件被创建" << name << std::endl; this->name = name; } ~ImageFile() override { std::cout << "图片文件被销毁" << name << std::endl; } // 以下三个方法体现的是:抽象构件类包含叶子构件行为和容器构件行为的并集 void add(AbstractFile* file) override { std::cout << "图片文件为叶子节点,不支持该方法!" << std::endl; } void remove(AbstractFile* file) override { std::cout << "图片文件为叶子节点,不支持该方法!" << std::endl; } void rename() override { // 重命名:叶子节点和容器节点都可以重命名 std::cout << "---对图片文件: " << name << " 进行重命名" << std::endl; } private: std::string name; }; class TextFile : public AbstractFile { public: TextFile(const std::string& name) { std::cout << "文本文件被创建" << name << std::endl; this->name = name; } ~TextFile() override { std::cout << "文本文件被销毁" << name << std::endl; } void add(AbstractFile* file) override { std::cout << "文本文件为叶子节点,不支持该方法!" << std::endl; } void remove(AbstractFile* file) override { std::cout << "文本文件为叶子节点,不支持该方法!" << std::endl; } void rename() override { std::cout << "---对文本文件: " << name << " 进行重命名" << std::endl; } private: std::string name; }; class VideoFile : public AbstractFile { public: VideoFile(const std::string& name) { std::cout << "视频文件被创建" << name << std::endl; this->name = name; } ~VideoFile() override { std::cout << "视频文件被销毁" << name << std::endl; } void add(AbstractFile* file) override { std::cout << "视频文件为叶子节点,不支持该方法!" << std::endl; } void remove(AbstractFile* file) override { std::cout << "视频文件为叶子节点,不支持该方法!" << std::endl; } void rename() override { std::cout << "---对视频文件: " << name << " 进行重命名" << std::endl; } private: std::string name; }; // Composite(容器构件) // Composite(容器构件): Folder class Folder : public AbstractFile { public: Folder(const std::string& name) { std::cout << "文件夹容器被创建" << name << std::endl; this->name = name; } ~Folder() override { std::cout << "文件夹容器被销毁" << name << std::endl; } // 叶子构件行为和容器构件行为的并集 void add(AbstractFile* file) override { fileList.push_back(file); } void remove(AbstractFile* file) override { fileList.remove(file); } void rename() override { std::cout << "对文件夹: " << name << " 进行重命名" << std::endl; //递归调用成员构件的 rename()方法 for (AbstractFile* obj : fileList) { obj->rename(); } } private: std::list fileList; std::string name; }; // 测试 int main() { std::cout << "网站:https://www.codecomeon.com/" << std::endl; std::cout << std::endl; std::cout << "=====透明组合模式=====" << std::endl; //针对抽象构件编程 AbstractFile* imageFile1, * imageFile2, * textFile1, * textFile2, * videoFile1, *videoFile2, * imageFolder, * textFolder, * videoFolder, * folder; folder = new Folder("根目录文件夹"); std::cout << std::endl; imageFolder = new Folder("图像文件夹"); textFolder = new Folder("文本文件夹"); videoFolder = new Folder("视频文件夹"); std::cout << std::endl; imageFile1 = new ImageFile("图片01.jpg"); imageFile2 = new ImageFile("图片02.gif"); std::cout << std::endl; textFile1 = new TextFile("文本01.txt"); textFile2 = new TextFile("文本02.doc"); std::cout << std::endl; videoFile1 = new VideoFile("视频01.rmvb"); videoFile2 = new VideoFile("视频02.rmvb"); imageFolder->add(imageFile1); imageFolder->add(imageFile2); textFolder->add(textFile1); textFolder->add(textFile2); videoFolder->add(videoFile1); videoFolder->add(videoFile2); std::cout << std::endl; folder->add(imageFolder); folder->add(textFolder); folder->add(videoFolder); //从 根目录文件夹 节点开始进行重命名 std::cout << "=====测试从根节点开始重命名=====" << std::endl; folder->rename(); std::cout << std::endl; std::cout << "=====测试叶子构建无对象=====" << std::endl; imageFile1->add(imageFile2); textFile1->remove(textFile2); std::cout << std::endl; std::cout << "=====清理资源=====" << std::endl; delete imageFile1; delete imageFile2; delete textFile1; delete textFile2; delete videoFile1; delete folder; delete imageFolder; delete textFolder; delete videoFolder; return 0; } ##### 执行输出 网站:https://www.codecomeon.com/ =====透明组合模式===== 文件夹容器被创建根目录文件夹 文件夹容器被创建图像文件夹 文件夹容器被创建文本文件夹 文件夹容器被创建视频文件夹 图片文件被创建图片01.jpg 图片文件被创建图片02.gif 文本文件被创建文本01.txt 文本文件被创建文本02.doc 视频文件被创建视频01.rmvb 视频文件被创建视频02.rmvb =====测试从根节点开始重命名===== 对文件夹: 根目录文件夹 进行重命名 对文件夹: 图像文件夹 进行重命名 ---对图片文件: 图片01.jpg 进行重命名 ---对图片文件: 图片02.gif 进行重命名 对文件夹: 文本文件夹 进行重命名 ---对文本文件: 文本01.txt 进行重命名 ---对文本文件: 文本02.doc 进行重命名 对文件夹: 视频文件夹 进行重命名 ---对视频文件: 视频01.rmvb 进行重命名 ---对视频文件: 视频02.rmvb 进行重命名 =====测试叶子构建无对象===== 图片文件为叶子节点,不支持该方法! 文本文件为叶子节点,不支持该方法! =====清理资源===== 图片文件被销毁图片01.jpg 图片文件被销毁图片02.gif 文本文件被销毁文本01.txt 文本文件被销毁文本02.doc 视频文件被销毁视频01.rmvb 文件夹容器被销毁根目录文件夹 文件夹容器被销毁图像文件夹 文件夹容器被销毁文本文件夹 文件夹容器被销毁视频文件夹 E:\DesignPatterns\DesignPatterns\Debug\DesignPatterns.exe (进程 25896)已退出,代码为 0。 按任意键关闭此窗口. . . #### 安全组合模式 ##### 代码 #include #include // 透明组合模式 // Component(抽象构件): AbstractFile class AbstractFile { public: virtual ~AbstractFile() = default; // 重命名接口(也只有重命名接口,无论容器还是叶子节点,都有该功能,取交集) virtual void rename() = 0; protected: AbstractFile() = default; }; // Leaf(叶子构件),没有子节点 // ImageFile 图片文件 // TextFile 文本文件 // VideoFile 视频文件 class ImageFile : public AbstractFile { public: ImageFile(const std::string& name) { std::cout << "图片文件被创建" << name << std::endl; this->name = name; } ~ImageFile() override { std::cout << "图片文件被销毁" << name << std::endl; } void rename() override { // 重命名:叶子节点和容器节点都可以重命名 std::cout << "---对图片文件: " << name << " 进行重命名" << std::endl; } private: std::string name; }; class TextFile : public AbstractFile { public: TextFile(const std::string& name) { std::cout << "文本文件被创建" << name << std::endl; this->name = name; } ~TextFile() override { std::cout << "文本文件被销毁" << name << std::endl; } void rename() override { std::cout << "---对文本文件: " << name << " 进行重命名" << std::endl; } private: std::string name; }; class VideoFile : public AbstractFile { public: VideoFile(const std::string& name) { std::cout << "视频文件被创建" << name << std::endl; this->name = name; } ~VideoFile() override { std::cout << "视频文件被销毁" << name << std::endl; } void rename() override { std::cout << "---对视频文件: " << name << " 进行重命名" << std::endl; } private: std::string name; }; // Composite(容器构件) // Composite(容器构件): Folder class Folder : public AbstractFile { public: Folder(const std::string& name) { std::cout << "文件夹容器被创建" << name << std::endl; this->name = name; } ~Folder() override { std::cout << "文件夹容器被销毁" << name << std::endl; } // 抽象构件类包含叶子构件行为和容器构件行为的交集 // 只放容器本有的方法 void add(AbstractFile* file) { fileList.push_back(file); } void remove(AbstractFile* file){ fileList.remove(file); } void rename() override { std::cout << "对文件夹: " << name << " 进行重命名" << std::endl; //递归调用成员构件的 rename()方法 for (AbstractFile* obj : fileList) { obj->rename(); } } private: std::list fileList; std::string name; }; // 测试 int main() { std::cout << "网站:https://www.codecomeon.com/" << std::endl; std::cout << std::endl; std::cout << "=====安全组合模式=====" << std::endl; //针对抽象构件编程 AbstractFile* imageFile1, * imageFile2, * textFile1, * textFile2, * videoFile1, * videoFile2; Folder *imageFolder, * textFolder, * videoFolder, * folder; folder = new Folder("根目录文件夹"); std::cout << std::endl; imageFolder = new Folder("图像文件夹"); textFolder = new Folder("文本文件夹"); videoFolder = new Folder("视频文件夹"); std::cout << std::endl; imageFile1 = new ImageFile("图片01.jpg"); imageFile2 = new ImageFile("图片02.gif"); std::cout << std::endl; textFile1 = new TextFile("文本01.txt"); textFile2 = new TextFile("文本02.doc"); std::cout << std::endl; videoFile1 = new VideoFile("视频01.rmvb"); videoFile2 = new VideoFile("视频02.rmvb"); imageFolder->add(imageFile1); imageFolder->add(imageFile2); textFolder->add(textFile1); textFolder->add(textFile2); videoFolder->add(videoFile1); videoFolder->add(videoFile2); std::cout << std::endl; folder->add(imageFolder); folder->add(textFolder); folder->add(videoFolder); //从 根目录文件夹 节点开始进行重命名 std::cout << "=====测试从根节点开始重命名=====" << std::endl; folder->rename(); std::cout << std::endl; std::cout << "=====清理资源=====" << std::endl; delete imageFile1; delete imageFile2; delete textFile1; delete textFile2; delete videoFile1; delete folder; delete imageFolder; delete textFolder; delete videoFolder; return 0; } ##### 执行输出 网站:https://www.codecomeon.com/ =====安全组合模式===== 文件夹容器被创建根目录文件夹 文件夹容器被创建图像文件夹 文件夹容器被创建文本文件夹 文件夹容器被创建视频文件夹 图片文件被创建图片01.jpg 图片文件被创建图片02.gif 文本文件被创建文本01.txt 文本文件被创建文本02.doc 视频文件被创建视频01.rmvb 视频文件被创建视频02.rmvb =====测试从根节点开始重命名===== 对文件夹: 根目录文件夹 进行重命名 对文件夹: 图像文件夹 进行重命名 ---对图片文件: 图片01.jpg 进行重命名 ---对图片文件: 图片02.gif 进行重命名 对文件夹: 文本文件夹 进行重命名 ---对文本文件: 文本01.txt 进行重命名 ---对文本文件: 文本02.doc 进行重命名 对文件夹: 视频文件夹 进行重命名 ---对视频文件: 视频01.rmvb 进行重命名 ---对视频文件: 视频02.rmvb 进行重命名 =====清理资源===== 图片文件被销毁图片01.jpg 图片文件被销毁图片02.gif 文本文件被销毁文本01.txt 文本文件被销毁文本02.doc 视频文件被销毁视频01.rmvb 文件夹容器被销毁根目录文件夹 文件夹容器被销毁图像文件夹 文件夹容器被销毁文本文件夹 文件夹容器被销毁视频文件夹 E:\DesignPatterns\DesignPatterns\Debug\DesignPatterns.exe (进程 24688)已退出,代码为 0。 按任意键关闭此窗口. . . 本文标题: C++设计模式-组合模式 本文作者: 夏至未至 发布时间: 2021年10月22日 16:00 最近更新: 2022年2月23日 22:56 原文链接: 许可协议: 署名-非商业性-禁止演绎 4.0 国际(CC BY-NC-ND 4.0) 请按协议转载并保留原文链接及作者 设计模式(25) 上一个 C++设计模式-装饰器模式 下一个 C++设计模式-桥接模式 当前文章评论暂未开放,请移步至留言处留言。