当前位置: 首页 > news >正文

结构型设计模式——组合模式

文章目录

    • 组合模式
      • 结构
      • 实现
      • 特点

组合模式

树形结构在软件中随处可见,例如操作系统中的目录结构、应用软件中的菜单、办公系统中的公司组织结构等等,如何运用面向对象的方式来处理这种树形结构是**组合模式(Composite Pattern)**需要解决的问

题。

组合模式描述了在对待一组对象实例的时候,使用以单个对象实例相同的方式对待。组合的目的是将对象“组合”成树形结构,以表示部分-整体层次结构。通过实现组合模式,客户端可以统一对待各个对象与组合。

组合模式又称为部分-整体(Part-Whole)模式,它将对象组织到树形结构中,可以用来描述整体和部分的关系。

结构

在组合模式结构图中包含如下几个角色:

  • Component(抽象构件):它可以是接口或抽象类,为叶子构件和容器构件对象声明接口,在该角色中可以包含所有子类共有行为的声明和实现。在抽象构件中定义了访问及管理它的子构件的方法,如增加子构件、删除子构件、获取子构件等。
  • Leaf(叶子构件):它在组合结构中表示叶子节点对象,叶子节点没有子节点,它实现了在抽象构件中定义的行为。对于那些访问及管理子构件的方法,可以通过异常等方式进行处理。
  • Composite(容器构件):它在组合结构中表示容器节点对象,容器节点包含子节点,其子节点可以是叶子节点,也可以是容器节点,它提供一个集合用于存储子节点,实现了在抽象构件中定义的行为,包括那些访问及管理子构件的方法,在其业务方法中可以递归调用其子节点的业务方法。

**组合模式的关键是定义了一个抽象构件类,它既可以代表叶子,又可以代表容器,而客户端针对该抽象构件类进行编程,无须知道它到底表示的是叶子还是容器,可以对其进行统一处理。**同时容器对象与抽象构件类之间还建立一个聚合关联关系,在容器对象中既可以包含叶子,也可以包含容器,以此实现递归组合,形成一个树形结构。

实现

为了能够更加清楚地描述出设计模式中的组合关系(不是UML中的组合关系),在AbstractTeam和ManagerTeam之间画了两条线:

  • 继承关系:对节点的操作使用的是抽象类中提供的接口,以保证操作的一致性
  • 聚合关系:ManagerTeam类型的节点还可以有子节点,父节点和子节点的之间的关系需要具体问题具体分析
    • 子节点跟随父节点一起销毁,二者就是组合关系(UML中的组合关系)
    • 子节点不跟随父节点一起销毁,二者就是聚合关系
    • 上面的程序中,在父节点的析构函数中没有销毁它管理的子节点,所以在上图中标记的是聚合关系
// 抽象节点classAbstractTeam{public://设置当前船队的名字AbstractTeam(string name):m_name(name){}// 设置父节点voidsetParent(AbstractTeam*node){m_parent=node;}//得到当前船队节点的父节点AbstractTeam*getParent(){returnm_parent;}//获得当前船队的名字stringgetName(){returnm_name;}virtualboolhasChild(){returnfalse;}//给当前番队添加一个子船队节点virtualvoidadd(AbstractTeam*node){}//跟当前番队删除一个子船队节点virtualvoidremove(AbstractTeam*node){}//当前番队和敌人战斗virtualvoidfight()=0;//显示当前番队的信息virtualvoiddisplay()=0;virtual~AbstractTeam(){}protected:string m_name;AbstractTeam*m_parent=nullptr;};
// 叶子节点的小队classLeafTeam:publicAbstractTeam{public:usingAbstractTeam::AbstractTeam;voidfight()override{cout<<m_parent->getName()+m_name+"与黑胡子的船员进行近距离肉搏战..."<<endl;}voiddisplay()override{cout<<"我是"<<m_parent->getName()<<"下属的"<<m_name<<endl;}~LeafTeam(){cout<<"我是"<<m_parent->getName()<<"下属的"<<m_name<<", 战斗已经结束, 拜拜..."<<endl;}};
// 管理者节点classManagerTeam:publicAbstractTeam{public:usingAbstractTeam::AbstractTeam;voidfight()override{cout<<m_name+"和黑胡子的恶魔果实能力者战斗!!!"<<endl;}//把当前番队的子节点存储到list中voidadd(AbstractTeam*node)override{node->setParent(this);m_children.push_back(node);}//把某一个子节点从当前番队的list中删除voidremove(AbstractTeam*node)override{node->setParent(nullptr);m_children.remove(node);}boolhasChild(){returntrue;}list<AbstractTeam*>getChildren(){returnm_children;}//遍历这个list容器中的节点voiddisplay(){string info=string();for(constautoitem:m_children){if(item==m_children.back()){info+=item->getName();}else{// 优先级: + > +=info+=item->getName()+", ";}}cout<<m_name+"的船队是【"<<info<<"】"<<endl;}~ManagerTeam(){cout<<"我是【"<<m_name<<"】战斗结束, 拜拜..."<<endl;}private://容器内存储的就是它的子节点对象list<AbstractTeam*>m_children;};
// 内存释放voidgameover(AbstractTeam*root){if(root==nullptr){return;}if(root&&root->hasChild()){ManagerTeam*team=dynamic_cast<ManagerTeam*>(root);list<AbstractTeam*>children=team->getChildren();for(constautoitem:children){gameover(item);}}deleteroot;}// 和黑胡子战斗voidfighting(){vector<string>nameList={"俊美海贼团","巴托俱乐部","八宝水军","艾迪欧海贼团","咚塔塔海贼团","巨兵海贼团","约塔玛利亚大船团"};// 根节点ManagerTeam*root=newManagerTeam("草帽海贼团");for(inti=0;i<nameList.size();++i){ManagerTeam*child=newManagerTeam(nameList.at(i));root->add(child);if(i==nameList.size()-1){// 给最后一个番队添加子船队for(intj=0;j<9;++j){LeafTeam*leaf=newLeafTeam("第"+to_string(j+1)+"番队");child->add(leaf);leaf->fight();leaf->display();}child->fight();child->display();}}root->fight();root->display();cout<<"===================================="<<endl;gameover(root);}intmain(){fighting();return0;}

特点

要优点

  • 组合模式可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次,它让客户端忽略了层次的差异,方便对整个层次结构进行控制。
  • 客户端可以一致地使用一个组合结构或其中单个对象,不必关心处理的是单个对象还是整个组合结构,简化了客户端代码。
  • 在组合模式中增加新的容器构件和叶子构件都很方便,无须对现有类库进行任何修改,符合“开闭原则”。
  • 组合模式为树形结构的面向对象实现提供了一种灵活的解决方案,通过叶子对象和容器对象的递归组合,可以形成复杂的树形结构,但对树形结构的控制却非常简单。

主要缺点

  • 在增加新构件时很难对容器中的构件类型进行限制。
  • 有时候我们希望一个容器中只能有某些特定类型的对象,例如在某个文件夹中只能包含文本文件,使用组合模式时,不能依赖类型系统来施加这些约束,因为它们都来自于相同的抽象层,在这种情况下,必须通过在运行时进行类型检查来实现,这个实现过程较为复杂。

适用环境

  • 如果你需要实现树状对象结构, 可以使用组合模式。组合模式为你提供了两种共享公共接口的基本元素类型: 简单叶节点和复杂容器。 容器中可以包含叶节点和其他容器。 这使得你可以构建树状嵌套递归对象结构。
  • 如果你希望客户端代码以相同方式处理简单和复杂元素, 可以使用该模式。组合模式中定义的所有元素共用同一个接口。 在这一接口的帮助下, 客户端不必在意其所使用的对象的具体类。
http://www.jsqmd.com/news/793930/

相关文章:

  • 报名CSGO/steam游戏搬砖项目前,这些内幕一定要了解
  • Taotoken Token Plan 套餐为高频用户带来的实际成本优化观察
  • 参考文献列表(近现代当代中国篇)
  • 如何用SketchUp STL插件轻松实现3D打印:从设计到实物的完整指南
  • OpenClaw + Claude Code 插件:多 Agent 协作开发,到底解决了什么,没解决什么?
  • 深度盘点2026广州个体户核定流程精选榜单:革新税务便捷申报新体验
  • TypeScript 泛型详解:定义、使用、特点优势、泛型约束与泛型数据类型
  • 软考分析师90天冲刺|DAY12·需求冲突处理策略
  • 聊聊我是怎么用Claude code来学习项目的吧
  • 隐藏在闲鱼暗网的暴利生意
  • Arm SME架构下的矩阵乘法优化实践
  • C++异步日志系统
  • Anaconda常用指令集
  • 家政派单小程序源头厂家
  • Ascend NPU高效无损压缩技术解析与优化
  • 启航 —— 二本NPC程序学习之路
  • 从零构建轻量级AI网关:统一管理多模型服务实战指南
  • JavaWeb应用项目开发学习心得:深耕技术体系,践行工程化开发,筑牢企业级Web开发根基
  • 当出海合规压力持续上升时,多云服务容易忽略哪些细节
  • 快图设计:5个理由告诉你为什么这款Vue图片编辑器值得尝试
  • 邮件定时群发系统 - 开源邮件营销平台 | 支持定时发送、联系人管理、数据追踪
  • GPU加速Zak-OTFS调制技术解析与工程实践
  • Java 面向对象-上
  • Error response from daemon: client version 1.52 is too new. Maximum supported API version is 1.43
  • 【测试】之概念篇
  • 小白通俗易懂吃透XXL-JOB:从原理到架构,一篇就够
  • 手把手教你做——助睿实验作业1-订单利润分流数据加工(零代码ETL + 多表关联 + 条件分流),附完整操作步骤
  • Diablo Edit2:暗黑破坏神2角色编辑器完全指南,3步打造完美游戏体验
  • 基于Vue3的一站式AI服务聚合平台开发与部署实战
  • 对比自行搭建代理与使用Taotoken聚合服务在维护精力上的感受