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

设计模式 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.定义 组合模式&#xff08;Composite Pattern&#xff09;&#xff0c;又叫部分整体模式&#xff0c;是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象&#xff0c;用来表示部分以及整体层次。这种类型的设…...

【经典论文阅读10】MNS采样——召回双塔模型的最佳拍档

这篇发表于2020 WWW 上的会议论文&#xff0c;提出一种MNS方式的负样本采样方法。众所周知&#xff0c;MF方法难以解决冷启动问题&#xff0c;于是进化出双塔模型&#xff0c;但是以双塔模型为基础的召回模型的好坏十分依赖负样本的选取。为了解决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)

什么是事务&#xff1f; 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、功能简介 &#xff08;1&#xff09;用户注册与用户登录 &#xff08;2&#xff09;手机搜索、手机比拼、手机个性化推荐 &#xff08;3&#xff09;点击搜索的手机图片会就用户行为&#xff0c;轮播展示用户行为&#xff0c;推荐点击次数靠前的手机 &#xff08;4&#xf…...

Leetcode 环形链表|| 快慢指针解法

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

出书,是「盖你自己的房子」你知道吗?

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

深入探索MySQL SELECT查询:从基础到高级,解锁数据宝藏的密钥

系列文章目录 更新ing... MySQL操作全攻略&#xff1a;库、表、数据、事务全面指南深入探索MySQL SELECT查询&#xff1a;从基础到高级&#xff0c;解锁数据宝藏的密钥MySQL SELECT查询实战&#xff1a;练习题精选&#xff0c;提升你的数据库查询技能PyMySQL&#xff1a;连接P…...

驾校管理系统-手把手调试搭建

驾校管理系统-手把手调试搭建 驾校管理系统-手把手调试搭建...

知能行——考研数学利器

知能行使用体验全记录 首先&#xff0c;我先介绍一下自己&#xff0c;我是2018级的&#xff0c;2022年6月毕业&#xff0c;本科沈阳工业大学&#xff08;双非&#xff09;&#xff0c;今年二战&#xff0c;专业课自动控制原理&#xff0c;数二英二&#xff0c;目标是江南大学控…...

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 新特性&#xff1a;深入理解 Lambda 表达式的强大与应用 Lambda 表达式是 Java 8 引入的重要特性之一&#xff0c;它允许将匿名函数&#xff08;即无名称的函数&#xff09;作为参数传递给方法&#xff0c;简化了代码的编写&#xff0c;使代码更加简洁和易读。本文将深…...

HTML5 Canvas图形绘制技术应用

HTML5 Canvas图形绘制技术应用 目录 Canvas基础知识基本绘图操作路径操作文本绘制图像绘制变换复合图形与剪切阴影渐变动画与交互高级技巧...

JMETER工具:以录制手机app为例

JMETER工具&#xff1a;以录制手机app为例子 JMETER安装和环境配置 pc需要安装jdk&#xff0c;并进行jdk的环境配置&#xff0c;安装好jdk并配置好后&#xff0c;通过命令行输入java –version出现以下界面就表示安装成功&#xff1a; &#xff08;对应的jdk版本不可太低&…...

PDF文件权限密码保护:如何去除及解决方法

如果你忘记了PDF文件密码&#xff0c;不用担心&#xff01;PDF解密、找回密码、去除密码的方法简单易行。只需两步&#xff1a;1、打开百度搜索“密码帝官网”&#xff1b;2、在官网页面点击“立即开始”&#xff0c;上传文件&#xff0c;稍等片刻即可找回密码。这种方法安全、…...

【电子信息(工程)】电子通信创新创业教育综合

电子通信创新创业教育 阐述电磁场、电磁波和电磁频谱及应用一、电磁场 法拉第根据电流与磁场的关系,提出了电磁感应定律:如果电磁场中有处于运动状态下的闭合回路导体存在,流经该导体磁场的磁场强度和磁场量,通常都会出现相应的变化,电磁感应电流由此而产生。随后,英国的…...

光伏无人机巡检的工作原理是什么?

随着科技的飞速发展&#xff0c;无人机技术已经深入到众多领域&#xff0c;其中光伏电站的巡检工作便是其应用的一个重要方向。光伏无人机巡检&#xff0c;通过搭载各种先进的传感器和设备&#xff0c;对光伏电站进行全面的、高效的、安全的检测&#xff0c;为电站的运维管理提…...

泛型中K T V E ? Object等分别代表的含义

E – Element (在集合中使用&#xff0c;因为集合中存放的是元素) T – Type&#xff08;Java 类&#xff09; K – Key&#xff08;键&#xff09; V – Value&#xff08;值&#xff09; N – Number&#xff08;数值类型&#xff09; &#xff1f; – 表示不确定的java类型&…...

C++实现分布式网络通信框架RPC(3)--rpc调用端

目录 一、前言 二、UserServiceRpc_Stub 三、 CallMethod方法的重写 头文件 实现 四、rpc调用端的调用 实现 五、 google::protobuf::RpcController *controller 头文件 实现 六、总结 一、前言 在前边的文章中&#xff0c;我们已经大致实现了rpc服务端的各项功能代…...

Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误

HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误&#xff0c;它们的含义、原因和解决方法都有显著区别。以下是详细对比&#xff1a; 1. HTTP 406 (Not Acceptable) 含义&#xff1a; 客户端请求的内容类型与服务器支持的内容类型不匹…...

三维GIS开发cesium智慧地铁教程(5)Cesium相机控制

一、环境搭建 <script src"../cesium1.99/Build/Cesium/Cesium.js"></script> <link rel"stylesheet" href"../cesium1.99/Build/Cesium/Widgets/widgets.css"> 关键配置点&#xff1a; 路径验证&#xff1a;确保相对路径.…...

相机Camera日志实例分析之二:相机Camx【专业模式开启直方图拍照】单帧流程日志详解

【关注我&#xff0c;后续持续新增专题博文&#xff0c;谢谢&#xff01;&#xff01;&#xff01;】 上一篇我们讲了&#xff1a; 这一篇我们开始讲&#xff1a; 目录 一、场景操作步骤 二、日志基础关键字分级如下 三、场景日志如下&#xff1a; 一、场景操作步骤 操作步…...

postgresql|数据库|只读用户的创建和删除(备忘)

CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...

解决本地部署 SmolVLM2 大语言模型运行 flash-attn 报错

出现的问题 安装 flash-attn 会一直卡在 build 那一步或者运行报错 解决办法 是因为你安装的 flash-attn 版本没有对应上&#xff0c;所以报错&#xff0c;到 https://github.com/Dao-AILab/flash-attention/releases 下载对应版本&#xff0c;cu、torch、cp 的版本一定要对…...

MySQL 8.0 OCP 英文题库解析(十三)

Oracle 为庆祝 MySQL 30 周年&#xff0c;截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始&#xff0c;将英文题库免费公布出来&#xff0c;并进行解析&#xff0c;帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...

成都鼎讯硬核科技!雷达目标与干扰模拟器,以卓越性能制胜电磁频谱战

在现代战争中&#xff0c;电磁频谱已成为继陆、海、空、天之后的 “第五维战场”&#xff0c;雷达作为电磁频谱领域的关键装备&#xff0c;其干扰与抗干扰能力的较量&#xff0c;直接影响着战争的胜负走向。由成都鼎讯科技匠心打造的雷达目标与干扰模拟器&#xff0c;凭借数字射…...

Android第十三次面试总结(四大 组件基础)

Activity生命周期和四大启动模式详解 一、Activity 生命周期 Activity 的生命周期由一系列回调方法组成&#xff0c;用于管理其创建、可见性、焦点和销毁过程。以下是核心方法及其调用时机&#xff1a; ​onCreate()​​ ​调用时机​&#xff1a;Activity 首次创建时调用。​…...

华硕a豆14 Air香氛版,美学与科技的馨香融合

在快节奏的现代生活中&#xff0c;我们渴望一个能激发创想、愉悦感官的工作与生活伙伴&#xff0c;它不仅是冰冷的科技工具&#xff0c;更能触动我们内心深处的细腻情感。正是在这样的期许下&#xff0c;华硕a豆14 Air香氛版翩然而至&#xff0c;它以一种前所未有的方式&#x…...