设计模式 17 组合模式 Composite Pattern
设计模式 17 组合模式 Composite Pattern
1.定义
组合模式(Composite Pattern),又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。
这种模式创建了一个包含自己对象组的类。该类提供了修改相同对象组的方式。它允许你将对象组合成树形结构,以表示“部分-整体”层次关系。它将单个对象和组合对象都视为相同类型的对象,从而使你能够统一地处理它们
核心思想:
将单个对象和组合对象都视为相同类型的对象,即它们都实现相同的接口或抽象类。
组合对象可以包含其他对象,形成树形结构。
客户端代码可以统一地处理单个对象和组合对象。
2.内涵
组合模式的内涵在于它提供了一种 统一处理单个对象和组合对象 的方法,从而简化代码、提高可扩展性和可重用性。
核心内涵
- 树形结构: 组合模式的核心是构建一个树形结构,以表示“部分-整体”层次关系。树的节点可以是单个对象(叶子节点)或组合对象(非叶子节点)。
- 统一接口: 组合模式要求所有组件(包括单个对象和组合对象)都实现相同的接口或抽象类。这使得客户端代码可以统一地处理所有组件,而无需关心它们是单个对象还是组合对象。
- 递归操作: 组合模式通常使用递归来处理组合对象。当对组合对象进行操作时,它会递归地对它的子组件进行相同操作。
- 简化代码: 由于所有组件都具有相同的接口,客户端代码可以统一地处理它们,避免了对不同类型对象的特殊处理。
- 提高可扩展性: 可以轻松地添加新的组件类型,而无需修改现有代码。因为新的组件只需要实现相同的接口即可。
- 增强灵活性和可重用性: 可以灵活地组合不同的组件,以创建不同的结构,并可以将这些结构重用在不同的场景中
3.案例分析
#include <algorithm>
#include <iostream>
#include <list>
#include <string>class Component {/*** @var Component*/protected:Component *parent_;public:virtual ~Component() {}void SetParent(Component *parent) {this->parent_ = parent;}Component *GetParent() const {return this->parent_;}virtual void Add(Component *component) {}virtual void Remove(Component *component) {}virtual bool IsComposite() const {return false;}virtual std::string Operation() const = 0;
};class Leaf : public Component {public:std::string Operation() const override {return "Leaf";}
};class Composite : public Component {protected:std::list<Component *> children_;public:void Add(Component *component) override {this->children_.push_back(component);component->SetParent(this);}void Remove(Component *component) override {children_.remove(component);component->SetParent(nullptr);}bool IsComposite() const override {return true;}std::string Operation() const override {std::string result;for (const Component *c : children_) {if (c == children_.back()) {result += c->Operation();} else {result += c->Operation() + "+";}}return "Branch(" + result + ")";}
};void ClientCode(Component *component) {// ...std::cout << "RESULT: " << component->Operation();// ...
}void ClientCode2(Component *component1, Component *component2) {// ...if (component1->IsComposite()) {component1->Add(component2);}std::cout << "RESULT: " << component1->Operation();// ...
}int main() {Component *simple = new Leaf;std::cout << "Client: I've got a simple component:\n";ClientCode(simple);std::cout << "\n\n";Component *tree = new Composite;Component *branch1 = new Composite;Component *leaf_1 = new Leaf;Component *leaf_2 = new Leaf;Component *leaf_3 = new Leaf;branch1->Add(leaf_1);branch1->Add(leaf_2);Component *branch2 = new Composite;branch2->Add(leaf_3);tree->Add(branch1);tree->Add(branch2);std::cout << "Client: Now I've got a composite tree:\n";ClientCode(tree);std::cout << "\n\n";std::cout << "Client: I don't need to check the components classes even when managing the tree:\n";ClientCode2(tree, simple);std::cout << "\n";delete simple;delete tree;delete branch1;delete branch2;delete leaf_1;delete leaf_2;delete leaf_3;return 0;
}
以上代码UML图如下所示:
4.注意事项
在使用组合模式进行开发时,需要考虑以下几个注意事项:
1. 避免循环引用:
组合模式中,组件之间可以相互嵌套,形成树形结构。如果出现循环引用,会导致无限递归,最终导致程序崩溃。
例如,文件夹 A 包含文件夹 B,文件夹 B 又包含文件夹 A,就会形成循环引用。
避免循环引用的方法是仔细设计组件之间的关系,确保没有相互依赖的循环。
2. 谨慎使用递归:
组合模式中,通常使用递归来处理组合对象。递归虽然方便,但可能会导致栈溢出,尤其是在处理大型树形结构时。
为了避免栈溢出,可以考虑使用迭代的方式来代替递归,或者使用尾递归优化。
3. 考虑性能:
组合模式中,对组合对象的访问可能会涉及多个子组件的访问,因此需要考虑性能问题。
为了提高性能,可以考虑使用缓存机制,或者使用更轻量级的结构来代替树形结构。
4. 确保接口的完整性:
组合模式中,所有组件都必须实现相同的接口。因此,需要确保接口的完整性,包含所有必要的操作方法。
接口应该尽可能地抽象,避免与具体实现细节相关联。
5. 避免过度使用:
组合模式是一种强大的模式,但它并不适合所有场景。如果你的系统结构比较简单,或者没有明显的“部分-整体”层次关系,则不需要使用组合模式。
在选择设计模式时,需要权衡利弊,选择最适合的模式
5.最佳实践
组合模式是一个强大的工具,但要有效地运用它,需要遵循一些最佳实践:
1. 明确“部分-整体”层次关系:
首先,要确保你的系统中存在明显的“部分-整体”层次关系。例如,文件系统中的文件夹和文件,组织结构中的部门和员工,图形界面中的容器和组件等。
只有在存在这种层次关系的情况下,组合模式才能发挥其优势。
2. 设计清晰的组件接口:
定义一个抽象的 Component 接口,所有组件(包括单个对象和组合对象)都必须实现这个接口。
接口应该包含所有必要的操作方法,例如 add(), remove(), getChild(), getName(), getSize() 等,这些方法应该能够适用于所有类型的组件。
3. 确保接口的完整性:
接口应该尽可能地抽象,避免与具体实现细节相关联。
同时,接口应该包含所有必要的操作方法,以支持所有可能的用例。
4. 谨慎使用递归:
递归是处理组合对象的一种常见方式,但它可能会导致栈溢出,尤其是在处理大型树形结构时。
可以考虑使用迭代的方式来代替递归,或者使用尾递归优化。
5. 考虑性能:
在处理大型树形结构时,性能是一个重要因素。
可以考虑使用缓存机制,或者使用更轻量级的结构来代替树形结构。
6. 避免过度使用:
组合模式并不适合所有场景。如果你的系统结构比较简单,或者没有明显的“部分-整体”层次关系,则不需要使用组合模式。
在选择设计模式时,需要权衡利弊,选择最适合的模式。
7. 使用示例代码进行验证:
在实际应用中,可以使用示例代码来验证组合模式的实现是否符合预期。
通过测试用例,可以确保组合模式能够正确地处理各种情况。
6.总结
组合模式的内涵在于它通过统一接口和递归操作,将单个对象和组合对象统一起来,简化了代码,提高了可扩展性和可重用性。它为构建灵活、可扩展和可重用的树形结构提供了强大的支持。
相关文章:

设计模式 17 组合模式 Composite Pattern
设计模式 17 组合模式 Composite Pattern 1.定义 组合模式(Composite Pattern),又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。这种类型的设…...

【经典论文阅读10】MNS采样——召回双塔模型的最佳拍档
这篇发表于2020 WWW 上的会议论文,提出一种MNS方式的负样本采样方法。众所周知,MF方法难以解决冷启动问题,于是进化出双塔模型,但是以双塔模型为基础的召回模型的好坏十分依赖负样本的选取。为了解决Batch内负样本带来的选择性偏差…...
串行低功耗芯片间媒体总线(SLIMbus)介绍
文章目录 SLIMbus简介slimbus设备和设备类Manager DeviceFramer DeviceInterface DeviceGeneric Device (Function)SLIMbus组件简单的SLIMbus组件复杂的SLIMbus组件SLIMbus的DATA和CLKSLIMbus的Clock Frequencies和GearsCells, Slots, Subframes, Frames, and...
esp32-S3 使用自带的大模型,实现本地文字转语言tts
目录 CMakeLists.txt文件中: 初始化以及实际运用代码: 在partitions.csv 内存分配文件中,添加voice_data项...
Redis事务(1)
什么是事务? Redis 的事务和 MySQL 的事务概念上是类似的. 都是把⼀系列操作绑定成⼀组. 让这⼀组能够批量执行。 但是注意体会 Redis 的事务和 MySQL 事务的区别: 弱化的原⼦性: redis 没有 “回滚机制”. 只能做到这些操作 “批量执⾏”. 不能做到 “⼀个失败就…...
202206青少年软件编程(Python)等级考试试卷(四级)
第 1 题 【单选题】 有如下 Python 程序, 包含 lambda 函数, 运行该程序后, 输出的结果是? ( ) g = lambda x,y:x*yprint(g(2,3))A :2 B :3 C :6 D :8 正确答案:C 试题解析: g = lambda x, y: x*y, lambda 函数返回参数 x 和 y 的积, 因此选 C。 第 2 题 【单选…...

大作业爬取手机数据,实现手机推荐系统以及朋友圈手机论坛
1、功能简介 (1)用户注册与用户登录 (2)手机搜索、手机比拼、手机个性化推荐 (3)点击搜索的手机图片会就用户行为,轮播展示用户行为,推荐点击次数靠前的手机 (4…...

Leetcode 环形链表|| 快慢指针解法
但是我们不知道 aaa 的值,该怎么办?依然是使用双指针法。考虑构建一个指针,此指针需要有以下性质:此指针和 slow 一起向前走 a 步后,两者在入口节点重合。那么从哪里走到入口节点需要 aaa 步?答案是链表头节…...

出书,是「盖你自己的房子」你知道吗?
出书是「盖你自己的房子」 尊敬的出书盟友: 你好!我希望这封信能够激发您对出书和阅读的热情。 在当今信息爆炸的时代,每个人都有机会分享自己的故事、思想和知识。而书籍作为一种流传百年的媒体,依旧承载着无限的力量和影响力…...

深入探索MySQL SELECT查询:从基础到高级,解锁数据宝藏的密钥
系列文章目录 更新ing... MySQL操作全攻略:库、表、数据、事务全面指南深入探索MySQL SELECT查询:从基础到高级,解锁数据宝藏的密钥MySQL SELECT查询实战:练习题精选,提升你的数据库查询技能PyMySQL:连接P…...
驾校管理系统-手把手调试搭建
驾校管理系统-手把手调试搭建 驾校管理系统-手把手调试搭建...

知能行——考研数学利器
知能行使用体验全记录 首先,我先介绍一下自己,我是2018级的,2022年6月毕业,本科沈阳工业大学(双非),今年二战,专业课自动控制原理,数二英二,目标是江南大学控…...
pod 库发布脚本
repo_tag.sh 文件 #!/bin/zsh# 私有库名称 #PODNAME${PWD##*/} PODNAME"LBHorizontalCenterLayout"function obtain_git_tag {# 类似 "s.version 0.0.1"VERSION_STRINGgrep -E s.version.* ${PODNAME}.podspecTAGtr -cd "[0-9.]" <<&…...
Java 8 新特性:深入理解 Lambda 表达式的强大与应用
Java 8 新特性:深入理解 Lambda 表达式的强大与应用 Lambda 表达式是 Java 8 引入的重要特性之一,它允许将匿名函数(即无名称的函数)作为参数传递给方法,简化了代码的编写,使代码更加简洁和易读。本文将深…...
HTML5 Canvas图形绘制技术应用
HTML5 Canvas图形绘制技术应用 目录 Canvas基础知识基本绘图操作路径操作文本绘制图像绘制变换复合图形与剪切阴影渐变动画与交互高级技巧...
JMETER工具:以录制手机app为例
JMETER工具:以录制手机app为例子 JMETER安装和环境配置 pc需要安装jdk,并进行jdk的环境配置,安装好jdk并配置好后,通过命令行输入java –version出现以下界面就表示安装成功: (对应的jdk版本不可太低&…...
PDF文件权限密码保护:如何去除及解决方法
如果你忘记了PDF文件密码,不用担心!PDF解密、找回密码、去除密码的方法简单易行。只需两步:1、打开百度搜索“密码帝官网”;2、在官网页面点击“立即开始”,上传文件,稍等片刻即可找回密码。这种方法安全、…...
【电子信息(工程)】电子通信创新创业教育综合
电子通信创新创业教育 阐述电磁场、电磁波和电磁频谱及应用一、电磁场 法拉第根据电流与磁场的关系,提出了电磁感应定律:如果电磁场中有处于运动状态下的闭合回路导体存在,流经该导体磁场的磁场强度和磁场量,通常都会出现相应的变化,电磁感应电流由此而产生。随后,英国的…...

光伏无人机巡检的工作原理是什么?
随着科技的飞速发展,无人机技术已经深入到众多领域,其中光伏电站的巡检工作便是其应用的一个重要方向。光伏无人机巡检,通过搭载各种先进的传感器和设备,对光伏电站进行全面的、高效的、安全的检测,为电站的运维管理提…...
泛型中K T V E ? Object等分别代表的含义
E – Element (在集合中使用,因为集合中存放的是元素) T – Type(Java 类) K – Key(键) V – Value(值) N – Number(数值类型) ? – 表示不确定的java类型&…...

测试微信模版消息推送
进入“开发接口管理”--“公众平台测试账号”,无需申请公众账号、可在测试账号中体验并测试微信公众平台所有高级接口。 获取access_token: 自定义模版消息: 关注测试号:扫二维码关注测试号。 发送模版消息: import requests da…...

智能仓储的未来:自动化、AI与数据分析如何重塑物流中心
当仓库学会“思考”,物流的终极形态正在诞生 想象这样的场景: 凌晨3点,某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径;AI视觉系统在0.1秒内扫描包裹信息;数字孪生平台正模拟次日峰值流量压力…...
CMake控制VS2022项目文件分组
我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...

Unsafe Fileupload篇补充-木马的详细教程与木马分享(中国蚁剑方式)
在之前的皮卡丘靶场第九期Unsafe Fileupload篇中我们学习了木马的原理并且学了一个简单的木马文件 本期内容是为了更好的为大家解释木马(服务器方面的)的原理,连接,以及各种木马及连接工具的分享 文件木马:https://w…...

Mysql中select查询语句的执行过程
目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析(Parser) 2.4、执行sql 1. 预处理(Preprocessor) 2. 查询优化器(Optimizer) 3. 执行器…...

免费PDF转图片工具
免费PDF转图片工具 一款简单易用的PDF转图片工具,可以将PDF文件快速转换为高质量PNG图片。无需安装复杂的软件,也不需要在线上传文件,保护您的隐私。 工具截图 主要特点 🚀 快速转换:本地转换,无需等待上…...
Web中间件--tomcat学习
Web中间件–tomcat Java虚拟机详解 什么是JAVA虚拟机 Java虚拟机是一个抽象的计算机,它可以执行Java字节码。Java虚拟机是Java平台的一部分,Java平台由Java语言、Java API和Java虚拟机组成。Java虚拟机的主要作用是将Java字节码转换为机器代码&#x…...

软件工程 期末复习
瀑布模型:计划 螺旋模型:风险低 原型模型: 用户反馈 喷泉模型:代码复用 高内聚 低耦合:模块内部功能紧密 模块之间依赖程度小 高内聚:指的是一个模块内部的功能应该紧密相关。换句话说,一个模块应当只实现单一的功能…...
TJCTF 2025
还以为是天津的。这个比较容易,虽然绕了点弯,可还是把CP AK了,不过我会的别人也会,还是没啥名次。记录一下吧。 Crypto bacon-bits with open(flag.txt) as f: flag f.read().strip() with open(text.txt) as t: text t.read…...
CppCon 2015 学习:REFLECTION TECHNIQUES IN C++
关于 Reflection(反射) 这个概念,总结一下: Reflection(反射)是什么? 反射是对类型的自我检查能力(Introspection) 可以查看类的成员变量、成员函数等信息。反射允许枚…...