设计模式-结构型模式-组合模式
我们很容易将“组合模式”和“组合关系”搞混。组合模式最初只是用于解决树形结构的场景,更多的是处理对象组织结构之间的问题。而组合关系则是通过将不同对象封装起来完成一个统一功能.
组合模式介绍
组合模式(Composite Pattern) 的定义是:将对象组合成树形结构以表示整个部分的层次结构.组合模式可以让用户统一对待单个对象和对象的组合.
比如: windows操作系统中的目录结构,其实就是树形目录结构,通过tree命令实现树形结构展示.

在上图中包含了文件夹和文件两类不同元素,其中在文件夹中可以包含文件,还可以继续包含子文件夹.子文件夹中可以放入文件,也可以放入子文件夹. 文件夹形成了一种容器结构(树形结构),递归结构.

接着我们再来思考虽然文件夹和文件是不同类型的对象,但是他们有一个共性,就是 都可以被放入文件夹中. 其实文件和文件夹可以被当做是同一种对象看待.
组合模式其实就是将一组对象(文件夹和文件)组织成树形结构,以表示一种'部分-整体' 的层次结构,(目录与子目录的嵌套结构). 组合模式让客户端可以统一单个对象(文件)和组合对象(文件夹)的处理逻辑(递归遍历).
组合模式更像是一种数据结构和算法的抽象,其中数据可以表示成树这种数据结构,业务需求可以通过在树上的递归遍历算法来实现.
组合模式原理

组合模式主要包含三种角色:
-
抽象根节点(Component):定义系统各层次对象的共有方法和属性,可以预先定义一些默认行为和属性。
在该角色中可以包含所有子类共有行为的声明和实现.在抽象根节点中定义了访问及管理它的子构建的方法,如增加子节点、删除子节点、获取子节点等.
-
树枝节点(Composite):定义树枝节点的行为,存储子节点,组合树枝节点和叶子节点形成一个树形结构。
树枝节点可以包含树枝节点,也可以包含叶子节点,它其中有一个集合可以用于存储子节点,实现了在抽象根节点中定义的行为.包括那些访问及管理子构建的方法,在其业务方法中可以递归调用其子节点的业务方法.
-
叶子节点(Leaf):叶子节点对象,其下再无分支,是系统层次遍历的最小单位。
在组合结构中叶子节点没有子节点,它实现了在抽象根节点中定义的行为.
组合模式实现
组合模式的关键在于定义一个抽象根节点类,它既可以代表叶子,又可以代表树枝节点,客户端就是针对该抽象类进行编程,不需要知道它到底表示的是叶子还是容器,可以对其进行统一处理.
树枝节点对象和抽象根节点类之间建立了一个聚合关联关系,在树枝节点对象中既可以包含叶子节点,还可以继续包含树枝节点,以此实现递归组合,形成一个树形结构.
代码实现
/*** 抽象根节点* 对于客户端而言将针对抽象编程,无需关心其具体子类是容器构建还是叶子构建.**/
public abstract class Component {public abstract void add(Component c); //增加成员public abstract void remove(Component c); //删除成员public abstract Component getChild(int i); //获取成员public abstract void operation(); //业务方法}/*** 叶子节点* 叶子节点中不能包含子节点**/
public class Leaf extends Component {@Overridepublic void add(Component c) {//具体操作}@Overridepublic void remove(Component c) {//具体操作}@Overridepublic Component getChild(int i) {//具体操作return new Leaf();}@Overridepublic void operation() {//叶子节点具体业务方法}
}/*** 树枝节点* 容器对象,可以包含子节点**/
public class Composite extends Component {private ArrayList<Component> list = new ArrayList<>();@Overridepublic void add(Component c) {list.add(c);}@Overridepublic void remove(Component c) {list.remove(c);}@Overridepublic Component getChild(int i) {return (Component) list.get(i);}@Overridepublic void operation() {//在树枝节点中的业务方法,将递归调用其他节点中的operation() 方法for (Component component : list) {component.operation();}}
}
组合模式应用实例
下面我们通过一段程序来演示一下组合模式的使用. 程序的功能是列出某一目录下所有的文件和文件夹.类图如下:

我们按照下图的表示,进行文件和文件夹的构建.

Entry类: 抽象类,用来定义File类和Directory类的共性内容
/*** Entry抽象类,表示目录条目(文件+文件夹)的抽象类**/
public abstract class Entry {public abstract String getName(); //获取文件名public abstract int getSize(); //获取文件大小//添加文件夹或文件public abstract Entry add(Entry entry);//显示指定目录下的所有信息public abstract void printList(String prefix);@Overridepublic String toString() {return getName() + "(" +getSize() + ")";}
}
File类,叶子节点,表示文件.
/*** File类 表示文件**/
public class File extends Entry {private String name; //文件名private int size; //文件大小public File(String name, int size) {this.name = name;this.size = size;}@Overridepublic String getName() {return name;}@Overridepublic int getSize() {return size;}@Overridepublic Entry add(Entry entry) {return null;}@Overridepublic void printList(String prefix) {System.out.println(prefix + "/" + this);}}
Directory类,树枝节点,表示文件
/*** Directory表示文件夹**/
public class Directory extends Entry{//文件的名字private String name;//文件夹与文件的集合private ArrayList<Entry> directory = new ArrayList();//构造函数public Directory(String name) {this.name = name;}//获取文件名称@Overridepublic String getName() {return this.name;}/*** 获取文件大小* 1.如果entry对象是File类型,则调用getSize方法获取文件大小* 2.如果entry对象是Directory类型,会继续调用子文件夹的getSize方法,形成递归调用.*/@Overridepublic int getSize() {int size = 0;//遍历或者去文件大小for (Entry entry : directory) {size += entry.getSize();}return size;}@Overridepublic Entry add(Entry entry) {directory.add(entry);return this;}//显示目录@Overridepublic void printList(String prefix) {System.out.println("/" + this);for (Entry entry : directory) {entry.printList("/" + name);}}
}
测试
public class Client {public static void main(String[] args) {//根节点Directory rootDir = new Directory("root");//树枝节点Directory binDir = new Directory("bin");//向bin目录中添加叶子节点binDir.add(new File("vi",10000));binDir.add(new File("test",20000));Directory tmpDir = new Directory("tmp");Directory usrDir = new Directory("usr");Directory mysqlDir = new Directory("mysql");mysqlDir.add(new File("my.cnf",30));mysqlDir.add(new File("test.db",25000));usrDir.add(mysqlDir);rootDir.add(binDir);rootDir.add(tmpDir);rootDir.add(mysqlDir);rootDir.printList("");}
}
组合模式总结
1 ) 组合模式的分类
-
透明组合模式
透明组合模式中,抽象根节点角色中声明了所有用于管理成员对象的方法,比如在示例中
Component声明了add、remove、getChild方法,这样做的好处是确保所有的构建类都有相同的接口。透明组合模式也是组合模式的标准形式。透明组合模式的缺点是不够安全,因为叶子对象和容器对象在本质上是有区别的,叶子对象不可能有下一个层次的对象,即不可能包含成员对象,因此为其提供 add()、remove() 等方法是没有意义的,这在编译阶段不会出错,但在运行阶段如果调用这些方法可能会出错(如果没有提供相应的错误处理代码)

-
在安全组合模式中,在抽象构建角色中没有声明任何用于管理成员对象的方法,而是在树枝节点类中声明并实现这些方法。安全组合模式的缺点是不够透明,因为叶子构建和容器构建具有不同的方法,且容器构建中那些用于管理成员对象的方法没有在抽象构建类中定义,因此客户端不能完全针对抽象编程,必须有区别地对待叶子构建和容器构建。

2 ) 组合模式优点
-
组合模式可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次,它让客户端忽略了层次的差异,方便对整个层次结构进行控制。
-
客户端可以一致地使用一个组合结构或其中单个对象,不必关心处理的是单个对象还是整个组合结构,简化了客户端代码。
-
在组合模式中增加新的树枝节点和叶子节点都很方便,无须对现有类库进行任何修改,符合“开闭原则”。
-
组合模式为树形结构的面向对象实现提供了一种灵活的解决方案,通过叶子节点和树枝节点的递归组合,可以形成复杂的树形结构,但对树形结构的控制却非常简单。
3 ) 组合模式的缺点
-
使用组合模式的前提在于,你的业务场景必须能够表示成树形结构。所以,组合模式的应用场景也 比较局限,它并不是一种很常用的设计模式。
4 ) 组合模式使用场景分析
-
处理一个树形结构,比如,公司人员组织架构、订单信息等;
-
跨越多个层次结构聚合数据,比如,统计文件夹下文件总数;
-
统一处理一个结构中的多个对象,比如,遍历文件夹下所有 XML 类型文件内容。
相关文章:
设计模式-结构型模式-组合模式
我们很容易将“组合模式”和“组合关系”搞混。组合模式最初只是用于解决树形结构的场景,更多的是处理对象组织结构之间的问题。而组合关系则是通过将不同对象封装起来完成一个统一功能. 组合模式介绍 组合模式(Composite Pattern) 的定义是:将对象组合…...
VScode开发工具总结
目录 高效使用VScode工具 vscode 字体放大缩小快捷键 Vscode翻译插件推荐 code-translator插件编辑 VsCode自动格式化代码 高效使用VScode工具...
opencv 解码视频流,c++ 代码写法
#include <opencv2/opencv.hpp> #include <iostream> using namespace cv; using namespace std; int main() { // 打开视频流 VideoCapture cap("your_video_stream_url"); // 检查视频流是否成功打开 if (!cap.isOpened()) { …...
Android 12.0 修改wifi信号强度
1.前言 在12.0的系统rom产品定制化开发中,在进行产品开发中,对应系统定制会有各种各样的需求,对纯wifi产品而言,对于wifi要求也是越来越高,因此有客户要求对wifi信号强度做定制,修改信号强度来增强显示wifi信号,所以要对wifi显示信号强度的相关代码做修改 2.修改wifi…...
Linux——容器简介
1.容器技术 软件应用通常依赖运行时环境提供的其他库、配置文件或服务,在运行时环境安装在物理主机或虚拟机上运行的操作系统,同时应用依赖项也会随着该操作系统一起安装在主机上。 主要弊端是依赖项会受运行时环境的影响,引用所需的支持软件…...
CMOS图像传感器——pipeline像素控制
一、传统像素操作 传统CMOS图像传感器的芯片架构中,像素的控制信号从水平方向驱动,像素的源极跟随器输出电压垂直地输出到位于顶部和底部的模拟前端读出电路,其具体实现方式如下图所示,其中RST, TX和SEL是像素水平控制信号,像素输出电压PIX OUT垂直地传输到读出电路。 …...
AI工具(ChatGPT)常用指令,持续更新...
要国内使用AI工具,关注:码视野,回复:1002使用VensiGPT时,当你给的指令越精准,它回复就会越完美,例如,如果你要请它协助撰写文案,如果没有给与指定情景与目标,…...
36--Django-项目实战-全栈开发-基于django+drf+vue+elementUI企业级项目开发流程-前台项目准备
前台项目创建 1.命令行创建vue项目(参考使用Vue脚手架快速搭建项目) vue create 项目名2.安装插件vue.js 3.配置全局css,在assets/css/global.css /* 声明全局样式和项目的初始化样式 */ body, h1, h2, h3, h4, h5, h6, p, table, tr, td, ul, li, a, form, input, selec…...
游戏算法-游戏AI行为树,python实现
参考文章:Behavior trees for AI: How they work (gamedeveloper.com) 本文主要参考上述weizProject Zomboid 的开发者 Chris Simpson文章的概念,用伪代码实现代码例子 AI概述 游戏AI是对游戏内所有非玩家控制角色的行为进行研究和设计,使得游…...
【新2023Q2模拟题JAVA】华为OD机试 - 矩阵最值 or 计算二维矩阵的最大值
最近更新的博客 华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为od机试,独家整理 已参加机试人员的实战技巧本篇题解:矩阵最值 or 计算二维矩阵的最…...
递归过程与递归工作栈
首先了解一下任意两个函数之间进行调用的情况: 比如在a函数里面调用b函数,那么在使用b函数之前,计算机还会1.把实参,返回地址给复制下来(但只是复制而已,地址不是原来的。)如图: 此外2.为被调用…...
B 树的简单认识
理解 B 树的概念 B 树是一种自平衡的查找树,能够保持数据有序。这种数据结构能够让查找数据、顺序访问、插入数据及删除数据的动作,都能在对数时间内完成。 同一般的二叉查找树不同,B 树是一棵多路平衡查找树,其特性是ÿ…...
【大数据Hive3.x数仓开发】窗口函数案例:连续N次登录的用户;级联累加求和;分组TopN
文章目录1 统计连续N次登录的用户(N>2)自连接过滤实现窗口函数lead()实现2 级联累加求和自连接窗口函数sum()实现3 分组TopN问题对窗口函数的讲解part见:【大数据Hive3.x数仓开发】函数–窗口函数 1 统计连续N次登录的用户(N&…...
openpyxl库自动填充excel实例分享
openpyxl可以通过编写Python脚本实现自动化Excel操作,包括自动填充数据、格式化单元格、生成图表等操作。 以下是一个常见的自动化Excel操作示例: 自动填充数据: from openpyxl import Workbook from openpyxl.utils import get_column_l…...
ICLR2021清华团队做的知识蒸馏提升detector的点的工作paper 小陈读论文系列
这个作者栏目就是一个词 清爽 牛逼不需要花里胡哨哈哈 无疑是有点tian了哈哈 不重要 毕竟有机会研读 梦中情笑的paper 还是很感激的 真的 很清爽啊 很多KD的工作确实 在下游任务呢效果不是很好 然后就引出了自己的关于提升知识蒸馏在OD方面的工作 OD 首先就有两个问题 1.前…...
Java核心技术知识点笔记—集合框架
前言:Java最初版本只为最常用的数据结构提供了很少的一组类:Vector、Stack、Hashtable、BitSet和Enumeration接口。其中,Enumeration接口提供了一种用于访问任意容器中各个元素的抽象机制。与现代数据结构类库常见情况一样,Java集…...
Rsync数据同步工具
一、什么是Rsync Rsync是一款开源的,快速的,多功能的,可实现全量及增量(差异化备份)的本地或远程数据同步备份的优秀工具。 Rsync软件适用于Unix、Linux、Windows等多种操作系统。 (1)可使本地…...
redux小结
store.dispatch(action对象) 在 dispatch 中调用 action 方法返回 action 对象 // /actions/index.js /*** Action:* action本质上是一个 JS 对象;* 必须要包含 type 属性,否则会报错;* 只描述了有事情要发生,…...
【Python】【进阶篇】十、Pygame的Font文本和字体
目录十、Pygame的Font文本和字体10.1 font.SysFont()10.2 font.Font()10.3 字体对象方法十、Pygame的Font文本和字体 Pygame 通过pygame.font模块来创建一个字体对象,从而实现绘制文本的目的。 该模块的常用方法如下所示: 名称说明pygame.font.init()初…...
【从零开始学习 UVM】10.8、UVM TLM —— UVM TLM Example
文章目录 subComp1subComp2ComponentAsubComp3ComponentBTop Env/Test这个 UVM TLM 示例使用之前文章中讨论的 put 端口、TLM FIFO 和 get 端口来构建一个具有不同层次的 TLM 端口的测试台。 下面定义了一个名为Packet的类,作为从一个组件传输到另一个组件的数据项。这个类对象…...
(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)
题目:3442. 奇偶频次间的最大差值 I 思路 :哈希,时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况,哈希表这里用数组即可实现。 C版本: class Solution { public:int maxDifference(string s) {int a[26]…...
日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻
在如今就业市场竞争日益激烈的背景下,越来越多的求职者将目光投向了日本及中日双语岗位。但是,一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧?面对生疏的日语交流环境,即便提前恶补了…...
地震勘探——干扰波识别、井中地震时距曲线特点
目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波:可以用来解决所提出的地质任务的波;干扰波:所有妨碍辨认、追踪有效波的其他波。 地震勘探中,有效波和干扰波是相对的。例如,在反射波…...
C++_核心编程_多态案例二-制作饮品
#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为:煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...
微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】
微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来,Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...
【大模型RAG】Docker 一键部署 Milvus 完整攻略
本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装;只需暴露 19530(gRPC)与 9091(HTTP/WebUI)两个端口,即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...
如何在看板中有效管理突发紧急任务
在看板中有效管理突发紧急任务需要:设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP(Work-in-Progress)弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中,设立专门的紧急任务通道尤为重要,这能…...
关于 WASM:1. WASM 基础原理
一、WASM 简介 1.1 WebAssembly 是什么? WebAssembly(WASM) 是一种能在现代浏览器中高效运行的二进制指令格式,它不是传统的编程语言,而是一种 低级字节码格式,可由高级语言(如 C、C、Rust&am…...
Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理
引言 Bitmap(位图)是Android应用内存占用的“头号杀手”。一张1080P(1920x1080)的图片以ARGB_8888格式加载时,内存占用高达8MB(192010804字节)。据统计,超过60%的应用OOM崩溃与Bitm…...
HashMap中的put方法执行流程(流程图)
1 put操作整体流程 HashMap 的 put 操作是其最核心的功能之一。在 JDK 1.8 及以后版本中,其主要逻辑封装在 putVal 这个内部方法中。整个过程大致如下: 初始判断与哈希计算: 首先,putVal 方法会检查当前的 table(也就…...
