设计模式——组合模式

server/2024/12/26 1:37:56/

文章目录

  • 1.定义
  • 2. 结构组成
  • 3. 组合模式结构
  • 4. 示例代码
  • 5. 模式优势
  • 6. 应用场景

1.定义

组合模式是一种设计模式它允许将对象组合成树形结构来表示 “部分 - 整体” 的层次关系,使得客户端可以统一地处理单个对象和对象组合,而无需区分它们。

2. 结构组成

  • 组件(Component):
    • 定义:是组合模式中的抽象类或接口,它定义了组合对象和叶子对象的公共操作,为客户端提供统一的操作接口。
    • 常见方法
      • 操作方法:例如execute()或operation()等,在不同的实现类中会有不同的具体操作逻辑。
      • 管理子组件的方法:如add(Component component)用于添加子组件,remove(Component component)用于移除子组件。这些方法在叶子类中可能为空实现。
      • 判断是否为组合对象的方法:如isComposite()用于判断当前对象是否是组合对象。
  • 叶子(Leaf):
    • 定义:是组合模式中的基本对象,它没有子对象,实现了组件接口中定义的方法,完成具体的操作。
    • 特点
      • 叶子对象是树形结构中的叶子节点,是操作的实际执行者。
      • 由于没有子对象,其添加和移除子对象的方法通常是空的。
  • 组合(Composite):
    • 定义:是包含子对象的对象,它可以是其他组合对象或叶子对象。它实现了组件接口中的方法,并且在这些方法中调用其子对象的相应方法。
    • 内部结构:
      • 通常包含一个数据结构(如列表、数组等)来存储子组件,例如children。
      • 实现管理子组件的方法,如add和remove方法会操作内部的子组件存储结构。
      • 在操作方法(如execute)中,会遍历子组件并调用它们的操作方法,实现将操作委派给子组件的功能。
  • 客户端(Client):
    • 作用:是使用组合模式的代码部分,通过组件接口来操作组合对象和叶子对象,无需知道它们的具体类型。
    • 操作方式:
      • 客户端调用组件的操作方法,由具体的对象(叶子或组合)来执行实际的操作。
      • 例如,客户端可以调用component.operation(),而不需要关心component是叶子对象还是组合对象。

3. 组合模式结构

在这里插入图片描述

  1. 组件 (Component) 接口描述了树中简单项目和复杂项目所共有的操作。

  2. 叶节点 (Leaf) 是树的基本结构, 它不包含子项目。

    一般情况下, 叶节点最终会完成大部分的实际工作, 因为它们无法将工作指派给其他部分。

  3. 容器 (Container)——又名 “组合 (Composite)”——是包含叶节点或其他容器等子项目的单位。 容器不知道其子项目所属的具体类, 它只通过通用的组件接口与其子项目交互。

    容器接收到请求后会将工作分配给自己的子项目, 处理中间结果, 然后将最终结果返回给客户端。

  4. 客户端 (Client) 通过组件接口与所有项目交互。 因此, 客户端能以相同方式与树状结构中的简单或复杂项目交互。

4. 示例代码

#include <iostream>
#include <memory>
#include <list>using namespace std;class Component{
protected:shared_ptr<Component> parent;
public:Component(){ cout<< "Component Construct" <<endl; }~Component(){cout<< "Component Destruct" <<endl; }shared_ptr<Component> GetParent(){return this->parent;}void SetParent(shared_ptr<Component> parent){this->parent = parent;}virtual void Add(shared_ptr<Component>){cout<< "Default Add" <<endl;}virtual void Remove(shared_ptr<Component>){cout<< "Default Remove" <<endl;}virtual bool IsComposite() const{cout<< "Default IsComposite : false" <<endl; return false;}virtual string Operation() const = 0;
};class Leaf : public Component{
public:Leaf(){cout<< "Leaf Construct" <<endl;}~Leaf(){cout<< "Leaf Destruct" <<endl;}string Operation()const{return "Leaf";}       
};class Composite : public Component,public enable_shared_from_this<Composite>{
protected:list<shared_ptr<Component>> children_;
public:Composite(){cout<< "Composite Construct" <<endl;}~Composite(){cout<< "Composite Destruct" <<endl;}void Add(shared_ptr<Component> component){children_.push_back(component);component->SetParent(shared_from_this());}void Remove(shared_ptr<Component> component){children_.remove(component);component->SetParent(nullptr);}bool IsComposite() const{return true;}string Operation() const{string result;for(const shared_ptr<Component> c : children_){if(c == children_.back()){result += c->Operation();}else{result += c->Operation() + "+";}}return "Branch(" + result + ")";}
};void ClientCode(shared_ptr<Component> component){std::cout << "RESULT: " << component->Operation();
}void ClientCode2(shared_ptr<Component> component1,shared_ptr<Component> component2){if (component1->IsComposite()) {component1->Add(component2);}cout << "RESULT: " << component1->Operation();
}int main()
{shared_ptr<Component>  simple = make_shared<Leaf>();cout << "Client: I've got a simple component:\n";ClientCode(simple);cout << "\n\n";shared_ptr<Component> tree = make_shared<Composite>();shared_ptr<Component> branch1 = make_shared<Composite>();shared_ptr<Component> leaf_1 = make_shared<Leaf>();shared_ptr<Component> leaf_2 = make_shared<Leaf>();shared_ptr<Component> leaf_3 = make_shared<Leaf>();branch1->Add(leaf_1);branch1->Add(leaf_2);shared_ptr<Component> branch2 = make_shared<Composite>();branch2->Add(leaf_3);tree->Add(branch1);tree->Add(branch2);cout << "Client: Now I've got a composite tree:\n";ClientCode(tree);std::cout << "\n\n";cout << "Client: I don't need to check the components classes even when managing the tree:\n";ClientCode2(tree, simple);cout << "\n";return 0;
}

5. 模式优势

  1. 简化客户端代码
    • 客户端以统一的方式处理单个对象和组合对象,无需区分它们的具体类型,降低了客户端代码的复杂性。
  2. 易于扩展
    • 可以方便地添加新类型的组件(叶子或组合),只要实现了组件接口,就可以无缝地集成到现有的系统中,符合开闭原则。
  3. 层次结构清晰
    • 非常适合表示具有层次关系的数据结构,例如文件系统(文件和文件夹)、图形系统(简单图形和复合图形)等,使系统的层次结构更加清晰和易于理解。
  4. 代码复用性高
    • 叶子对象和组合对象都基于相同的组件接口,操作方法可以在不同场景下复用。

6. 应用场景

  1. 图形系统
    • 在图形系统中,简单图形(如圆形、矩形)可以作为叶子对象,而由多个图形组成的复杂图形可以作为组合对象。客户端可以统一地对它们进行绘制、移动等操作。
  2. 文件系统
    • 文件系统中的文件可以作为叶子对象,文件夹作为组合对象。操作系统的文件管理器(客户端)可以统一地操作文件和文件夹,如显示内容、复制、删除等操作。
  3. 组织结构
    • 在企业或组织的结构表示中,员工可以作为叶子对象,部门作为组合对象。可以方便地对组织进行管理和操作,如统计员工数量、计算部门绩效等。

http://www.ppmy.cn/server/153192.html

相关文章

深度解析 Pytest 中的 conftest.py

关注开源优测不迷路 大数据测试过程、策略及挑战 测试框架原理&#xff0c;构建成功的基石 在自动化测试工作之前&#xff0c;你应该知道的10条建议 在自动化测试中&#xff0c;重要的不是工具 在使用 Pytest 进行测试的过程中&#xff0c;conftest.py 文件扮演着极为重要的角色…

【bWAPP】XSS跨站脚本攻击实战

别低头&#xff0c;皇冠会掉&#xff1b;别流泪&#xff0c;贱人会笑。 0x01、XSS - Reflected (GET) Low 输入的内容直接输出到页面中: 后台服务端没有对输入的参数进行过滤, 构造一个注入xss payload即可: <script>alert(1)</script> 成功弹窗 Medium 审查…

【GIS教程】使用GDAL实现栅格转矢量(GeoJSON、Shapefile)- 附完整代码

文章目录 一、 应用场景1、GeoJSON2、ESRI Shapefile3、GDAL 二、基本思路1、数据准备2、重投影&#xff08;可选&#xff09;3、创建空的矢量图层4、栅格转矢量 三、完整代码四、总结五、拓展&#xff08;使用ArcGIS工具进行栅格转矢量&#xff09; 一、 应用场景 TIFF格式的…

2.利用docker进行gitlab服务器迁移

一、Docker安装 安装Ubuntu 22.04.3 LTS \n \l 1、旧版本安装包清理 sudo apt-get remove docker docker-engine docker.io containerd runc当你卸载Docker时&#xff0c;存储在/var/lib/docker/中的图像、容器、卷和网络不会自动删除。如果你想从一个干净的安装开始&#x…

详解Ollama api (Windows环境)

Ollama可以像一个本地软件一样在Windows中运行。通常通过以下两种方法来使用Windows中的Ollama。 在cmd或powershell中输入命令基于本地主机http://localhost:11434的Ollama API 关于Ollama的安装和命令行的使用方法可以参考我的另一篇博文。这篇博文主要介绍api的使用方法。…

Linux搭建TRELLIS详细流程

TRELLIS是最新的3D生成模型,官网地址如下: https://github.com/microsoft/TRELLIS 下面是详细的搭建流程。 由于是在Ubuntu系统上搭建的,下面操作仅限Ubuntu系统查看。 不过Windows系统大同小异,而且青龙大佬已经做了相关整理。 Windows系统的可以去看青龙大佬的b站视…

Vue.js前端框架教程11:Vue监听器watch和watchEffect

文章目录 监听器(watchers)基本用法deep: trueimmediate: true总结watchEffect基本用法自动追踪依赖停止监听与 `watch` 的对比性能优化总结监听器(watchers) 在 Vue 中,监听器(watchers)是一种观察和响应 Vue 实例上的数据变动的机制。Vue 提供了 watch 属性,允许我们…

运算符重载(一)

知识图谱 一.需要重载的原因 正常情况下&#xff0c;C 的运算符( 、-、*、/ 等)只能用于对基本类型的常量或变量进行运算&#xff0c;而不能用于类对象之间的运算。 类的对象直接的运算虽然可以通过成员函数或全局函数去实现&#xff0c;如date.Add(1)&#xff0c;但这样的写法…