设计模式——组合模式
什么是组合模式
组合模式(Composite Pattern):组合多个对象形成树形结构以表示具有“整体—部分”关系的层次结构。组合模式对单个对象(即叶子对象)和组合对象(即容器对象)的使用具有一致性,组合模式又可以称为“整体—部分”(Part-Whole)模式,它是一种对象结构型模式。
组合模式将对象组织到树结构中,可以用来描述整体与部分的关系,可以使客户端将单纯元素与复合元素同等看待。
树结构在过程性的编程语言中曾经发挥了巨大的作用,在面向对象的语言中,树结构也同样威力巨大。一个基于继承的类型的等级结构便是一个树结构;一个基于组合的对象结构也是一个树结构。
在树形结构中,最顶层的节点被称为根节点,根节点下面可以包含树枝节点和叶子节点,树枝节点下面又可以包含树枝节点和叶子节点,如下图所示:

由上图可以看出,其实根节点和树枝节点本质上属于同一种数据类型,可以作为容器使用;而叶子节点与树枝节点在语义上不属于用一种类型。但是在组合模式中,会把树枝节点和叶子节点看作属于同一种数据类型(用统一接口定义),让它们具备一致行为。
这样,在组合模式中,整个树形结构中的对象都属于同一种类型,带来的好处就是用户不需要辨别是树枝节点还是叶子节点,可以直接进行操作,给用户的使用带来极大的便利。
模式的结构
组合模式UML类图

UML类图讲解:
Component(抽象构件):它可以是接口或抽象类,为叶子构件和容器构件对象声明接口,在该角色中可以包含所有子类共有行为的声明和实现。在抽象构件中定义了访问及管理它的子构件的方法,如增加子构件、删除子构件、获取子构件等。
Leaf(叶子构件):它在组合结构中表示叶子节点对象,叶子节点没有子节点,它实现了在抽象构件中定义的行为。对于那些访问及管理子构件的方法,可以通过异常等方式进行处理。
Composite(容器构件):它在组合结构中表示容器节点对象,容器节点包含子节点,其子节点可以是叶子节点,也可以是容器节点,它提供一个集合用于存储子节点,实现了在抽象构件中定义的行为,包括那些访问及管理子构件的方法,在其业务方法中可以递归调用其子节点的业务方法。
优点和缺点
优点
组合模式的主要优点如下:
组合模式可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次,它让客户端忽略了层次的差异,方便对整个层次结构进行控制。
客户端可以一致地使用一个组合结构或其中单个对象,不必关心处理的是单个对象还是整个组合结构,简化了客户端代码。
在组合模式中增加新的容器构件和叶子构件都很方便,无须对现有类库进行任何修改,符合“开闭原则”。
组合模式为树形结构的面向对象实现提供了一种灵活的解决方案,通过叶子对象和容器对象的递归组合,可以形成复杂的树形结构,但对树形结构的控制却非常简单。
缺点
组合模式的主要缺点如下:
破坏了“单一职责原则”。
在增加新构件时很难对容器中的构件类型进行限制。有时候我们希望一个容器中只能有某些特定类型的对象,例如在某个文件夹中只能包含文本文件,使用组合模式时,不能依赖类型系统来施加这些约束,因为它们都来自于相同的抽象层,在这种情况下,必须通过在运行时进行类型检查来实现,这个实现过程较为复杂。
组合模式的实现根据所实现接口的区别分为透明式组合模式和安全式组合模式。
透明式
作为第一种选择,在Component里面声明所有的用来管理子类对象的方法,包括add()、remove(),以及 getChild()方法。这样做的好处是所有的构件类都有相同的接口。在客户端看来,树叶类对象与合成类对象的区别起码在接口层次上消失了,客户端可以同等地对待所有的对象。这就是透明形式的合成模式。
这个选择的缺点是不够安全,因为树叶类对象和合成类对象在本质上是有区别的。树叶类对象不可能有下一个层次的对象,因此add()、remove()以及 getChild()方法没有意义,但是在编译时期不会出错,而只会在运行时期才会出错。
透明式的组合模式要求所有的具体构件类,不论树枝构件还是树叶构件,都符合一个固定的接口,类图如下:

透明式组合模式涉及到抽象构件角色、树叶构件角色、树枝构件角色三种模式:
- 抽象构件(Component)角色:这是一个抽象角色,它给参加组合的对象规定一个接口,规范共有的接口及默认行为。这个接口可以用来管理所有的子对象,要提供一个接口以规范取得和管理下层组件的接口,包括 add()、remove()以及getChild()之类的方法。
- 树叶构件(Leaf〉角色:代表参加组合的树叶对象,定义出参加组合的原始对象的行为。树叶类会给出add()、remove()以及getChild()之类的用来管理子类对象的方法的平庸实现。
- 树枝构件(Composite)角色:代表参加组合的有子对象的对象,定义出这样的对象的行为。
我们都见过画图软件,一个绘图系统给出各种工具用来描绘线、长方形和原形等基本图形组成的图形。一个复杂的图形肯定是有这些基本的图形组成的。本模式我们就以这为例子来讲解。
由于一个复杂的图形是由基本图形组合而成的,因此,一个组合的图形应当有一个列表,存储对所有的基本图形对象的引用。复合图形的draw()方法在调用时,应当逐一调用所有列表上的基本图形对象的draw()方法。
透明形式的组合模式意味着不仅只有树枝构件角色才配备有管理聚集的方法,树叶构件也有这些方法,虽然树叶构件的这些方法是平庸的。透明式的组合模式的类图如下:

抽象构件角色:

树枝构件角色:
public class Picture extends Graphics {private Vector items = new Vector(10);//具体管理方法,增加一个子构件对象public void add(Graphics graphics){items.add(graphics);}//删除一个子构件对象public void remove(Graphics graphics){items.remove(graphics);}//返回一个子构件对象public .Graphics getChild(int i){return (Graphics) items.get(i);}@Overridepublic void draw() {for (int i = 0; i < items.size(); i++) {Graphics graphics = (Graphics) items.get(i);graphics.draw();}}
}
树叶构件角色:
package com.zeus;public class Line extends Graphics{@Overridevoid draw() {System.out.println("画了一条线");}@Overridevoid add() {}@Overridevoid remove() {}@OverrideGraphics getChild(int i) {return null;}
}package com.zeus;public class Circle extends Graphics{@Overridevoid draw() {System.out.println("画了一个圆形");}@Overridevoid add() {}@Overridevoid remove() {}@OverrideGraphics getChild(int i) {return null;}
}
package com.zeus;public class Rectangle extends Graphics{@Overridevoid draw() {System.out.println("画了一个长方形");}@Overridevoid add() {}@Overridevoid remove() {}@OverrideGraphics getChild(int i) {return null;}
}
测试:

打印的结果:
画了一个长方形
画了一条线
画了一个长方形
安全式
第二种选择是在 Composite类里面声明所有的用来管理子类对象的方法。这样的做法是安全的做法,因为树叶类型的对象根本就没有管理子类对象的方法,因此,如果客户端对树叶类对象使用这些方法时,程序会在编译时期出错。编译通不过,就不会出现运行时期错误。
这个选择的缺点是不够透明,因为树叶类和合成类将具有不同的接口。
安全式的组合模式要求管理具体的方法只出现在树枝构件类中,如下图所示:

安全式组合模式涉及到抽象构件角色、树叶构件角色、树枝构件角色这三个角色:
- 抽象构件角色(Component):这是一个抽象角色,他给参加组合的对象定义出公共的接口及其默认行为,可以用来管理所有的子对象。组合对象通常把它所包含的子对象当作类型为component的对象,在安全式的组合模式里,构件角色并不定义出管理子对象的方法
- 树叶构件角色(Leaf):树叶对象是没有下级子对象的对象,定义出参加组合的原始对象的行为
- 树枝构件角色(Composite):代表参加组合的有下级子对象的对象,树枝构件类给出所有的管理子对象的方法,如add(),remove()以及getChild()等方法
同样以上面的绘图系统为例子讲解安全式组合模式。安全式组合模式意味着只有树枝构件角色才能配备有管理聚集的方法,而树叶构件角色则没有这些方法。UML类图如下:

抽象构件角色:

树枝构件角色:
public class Picture extends Graphics{private Vector items = new Vector(10);//具体管理方法,增加一个子构件对象public void add(Graphics graphics){items.add(graphics);}//删除一个子构件对象public void remove(Graphics graphics){items.remove(graphics);}//返回一个子构件对象public Graphics getChild(int i){return (Graphics) items.get(i);}@Overridepublic void draw() {for (int i = 0; i < items.size(); i++) {Graphics graphics = (Graphics) items.get(i);graphics.draw();}}
}
树叶构件角色:

测试:

打印结果:
画了一个长方形。。。。
画了一条线。。。。
画了一个长方形。。。。
适用环境
在以下情况下可以考虑使用组合模式:
-
在具有整体和部分的层次结构中,希望通过一种方式忽略整体与部分的差异,客户端可以一致地对待它们。
-
在一个使用面向对象语言开发的系统中需要处理一个树形结构。
-
在一个系统中能够分离出叶子对象和容器对象,而且它们的类型不固定,需要增加一些新的类型。
相关文章:
设计模式——组合模式
什么是组合模式 组合模式(Composite Pattern):组合多个对象形成树形结构以表示具有“整体—部分”关系的层次结构。组合模式对单个对象(即叶子对象)和组合对象(即容器对象)的使用具有一致性,组合模式又可以…...
get属性是什么?有什么用?在什么场景用?get会被Json序列化?
在JavaScript中,对象的属性不仅可以是数据属性(即常规的键值对),还可以是访问器属性(accessor properties)。访问器属性不包含实际的数据值,而是定义了如何获取(get)和设…...
这可能是你看过最详细的 [八大排序算法]
排序算法 前置知识 [排序稳定性]一、直接插入排序二、希尔排序三、直接选择排序四、堆排序五、冒泡排序六、快速排序七、归并排序八、计数排序(非比较排序)排序复杂度和稳定性总结 前置知识 [排序稳定性] 假定在待排序的记录序列中,存在多个…...
docker的安装
CentOS7 安装 Docker 安装需要的软件包, yum-util 提供yum-config-manager功能,另两个是devicemapper驱动依赖 yum install -y yum-utils device-mapper-persistent-data lvm2 添加下载源 yum-config-manager --add-repo http://mirrors.aliyun.com/…...
【业务功能篇75】微服务项目环境搭建docker-mysql-redisSpringCloudAlibaba
项目环境准备 1.虚拟机环境 我们可以通过VMWare来安装,但是通过VMWare安装大家经常会碰到网络ip连接问题,为了减少额外的环境因素影响,Docker内容的讲解我们会通过VirtualBox结合Vagrant来安装虚拟机。 VirtualBox官网:https:/…...
学习笔记|认识数码管|控制原理|数码管实现0-9的显示|段码跟位码|STC32G单片机视频开发教程(冲哥)|第九集:数码管静态显示
文章目录 1.认识数码管2.控制原理十进制转换为任意进制其它进制转十进制 3.数码管实现0-9的显示1.用数组定义0-9的内码段码跟位码的区别2.尝试用延时实现0-9的循环显示3.用按键控制数字的加或者减。 总结课后练习: 1.认识数码管 数码管按段数可分为七段数码管和八段…...
CentOS 7/8 firewall 转发端口
#开启系统路由模式功能 echo net.ipv4.ip_forward1>>/etc/sysctl.conf sysctl -p #开启firewalld systemctl start firewalld 打开防火墙伪装IP # 检查是否允许伪装IP,返回no表示没开启,反之开启伪装IP firewall-cmd --query-masquerade #设置…...
mysql自动备份脚本
备份脚本 #!/bin/bash #author cheng #mysql数据自动备份 mysql_user“root” mysql_password“passwprd” mysql_host“localhost” mysql_port“3306” mysql_charset“utf8mb4” #备份文件存放地址(根据实际情况填写) backup_location/usr/cheng/msg_manager/sql #是否删…...
VUE笔记(九)vuex
一、vuex的简介 1、回顾组件之间的通讯 父组件向子组件通讯:通过props实现 子组件向父组件通讯:通过自定义事件($emit)方式来实现 兄弟组件之间的通讯:事件总线($eventBus)、订阅与发布方式来实现 跨级组件的通讯…...
Webpack高频面试题
Webpack高频面试题 1 谈谈你对webpack的看法 现在的前端网页功能丰富,特别是SPA(single page web application 单页应用)技术流行后,JavaScript的复杂度增加和需要一大堆依赖包,还需要解决Scss,Less……新…...
数字基带传输系统
文章目录 前言一、数字基带系统基本组成二、基本码型1、数字基带信号2、6 种基本码型 三、数字基带信号的频谱特性四、数字基带信号选码1、原则2、常用的传输码型①、AMI 码(传号交替反转码)②、 H D B 3 HDB_3 HDB3 码(3 阶高密度双极性码…...
FPGA使用MIG调用SODIMM内存条接口教程,提供vivado工程源码和技术支持
目录 1、前言免责声明 2、SODIMM内存条简介3、设计思路框架视频输入视频缓存MIG配置调用SODIMM内存条VGA时序视频输出 4、vivado工程详解5、上板调试验证6、福利:工程代码的获取 1、前言 FPGA应用中,数据缓存是一大重点,不管是图像处理还是A…...
深度学习数据预处理
参考文章:深度学习中的数据预处理方法总结 在深度学习中,数据预处理(preprocessing)的重要性体现在以下几个方面: 1、数据质量: 原始数据通常包含错误、缺失值、异常值和噪声。预处理能够检测和处理这些问…...
[C++] STL_vector 迭代器失效问题
文章目录 1、前言2、情况一:底层空间改变的操作3、情况二:指定位置元素的删除操作4、g编译器对迭代器失效检测4.1 扩容4.2 erase删除任意位置(非尾删)4.3 erase尾删 5、总结 1、前言 **迭代器的主要作用就是让算法能够不用关心底…...
C语言暑假刷题冲刺篇——day5
目录 一、选择题 二、编程题 🎈个人主页:库库的里昂 🎐CSDN新晋作者 🎉欢迎 👍点赞✍评论⭐收藏✨收录专栏:C语言每日一练✨相关专栏:代码小游戏、C语言初阶、C语言进阶🤝希望作者…...
若依Cloud集成Flowable6.7.2
项目简介 基于若依Cloud的Jove-Fast微服务项目,集成工作流flowable(接上篇文章) 若依Cloud集成积木报表 项目地址:https://gitee.com/wxjstudy/jove-fast 后端 新建模块 目录结构如下: 引入依赖 前提:引入依赖之前先配置好maven的setting.xml &…...
动态不确定性的动态S过程(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...
LoadRunner操作教程
日升时奋斗,日落时自省 目录 1、Virtual User Generator (VUG) 1.1、WebTours系统 1.1.1、WebTours启动 1.1.2、WebTours配置 1.2、脚本录制 1.3、编译 1.4、脚本运行 1.5、加强脚本 1.5.1、事务插入 1.5.2、插入集合点 1.5.3、参…...
.NET Core 实现日志打印输出在控制台应用程序中
在本文中,我们将探讨如何在 .NET Core 应用程序中将日志消息输出到控制台,从而更好地了解应用程序的运行状况。 .NET Core 实现日志打印输出在控制台应用程序中 在 .NET Core 中,日志输出打印是使用 Microsoft.Extensions.Logging 命名空间…...
Nginx正向代理与反向代理及Minio反向代理实操(三)
本文是对: Nginx安装及Minio集群反向动态代理配置(二) 文的进一步完善: 多台服务器间免密登录|免密拷贝 Cenos7 搭建Minio集群部署服务器(一) Cenos7 搭建Minio集群Nginx统一访问入口|反向动态代理(二) Spring Boot 与Minio整合实现文件上传与下载(三) CentOS7的journa…...
2026 国内 ChatGPT 镜像站推荐
📖 国内直接访问,支持 GPTs、绘图、文件分析,对话数据隔离 ✅ 写方案/周报,描述需求直接生成,5分钟搞定 ✅ 代码报错,粘贴进去秒出解决方案 ✅ 读文件/PDF,上传即可提问,不用逐字看…...
2026 年 5 月 AI 热点:大模型、硬件、人形机器人全面升级
一、大模型技术突破 | LLM Technology Breakthroughs 1.1 OpenAI GPT‑5.5 正式成为ChatGPT默认模型 | GPT‑5.5 Becomes ChatGPT Default Model 英文内容 | English On May 5, 2026, OpenAI officially rolled out GPT‑5.5 Instant as the new default model for ChatGPT, …...
感知与建图,为什么不能只跑一个 SLAM Demo?
一、核心问题机器人要稳定工作,需要把视觉、激光、IMU、模型结果和ROS2协同整合到一条完整链路里,而不是只依赖单一的SLAM Demo。二、为什么SLAM Demo不够用?Demo的局限性:SLAM Demo只能证明单点功能能跑,无法覆盖实际…...
AI工程实践简报:如何用高质量信号提升技术决策效率
1. 项目概述:一份真正“够用”的AI资讯简报,到底长什么样?“This AI newsletter is all you need #38”——光看标题,你可能以为这又是一份泛泛而谈的行业 roundup,或是堆砌热点、浮于表面的“信息快餐”。但作为连续三…...
我踩了N多劣质工具坑从嫌弃到真香,2026这款语音生成软件真后悔没早用
上周刚下班被leader留下来整理2小时项目评审会纪要,对着录音逐句暂停记,熬到八点半还错漏了三个核心需求;上个月做行业专家访谈,3小时录音来回听,耳朵疼得发胀还漏了嘉宾的核心观点;报了线上的产品进阶课&a…...
CPU核心存储架构:寄存器文件与SRAM的设计原理与应用对比
1. 项目概述:从“存储”到“访问”的核心差异在处理器设计的核心地带,有两个名字听起来很像、功能也似乎都是“存东西”的组件,却常常让刚入行的朋友感到困惑:Register File(寄存器文件)和 SRAM(…...
深入解析SAR ADC:从二分搜索原理到高精度数据采集实战
1. 项目概述:从“猜数字”游戏理解SAR ADC在模拟信号处理的世界里,我们常常需要将现实世界中连续变化的物理量(比如温度、声音、压力)转换成计算机能够理解和处理的数字信号。这个关键的桥梁,就是模数转换器。而在众多…...
通过curl命令快速测试Taotoken上不同大模型的响应效果
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 通过curl命令快速测试Taotoken上不同大模型的响应效果 对于开发者而言,在集成大模型能力时,快速验证接口连…...
3步彻底解决Windows更新后开始菜单重置难题:ExplorerPatcher深度解析与实战
3步彻底解决Windows更新后开始菜单重置难题:ExplorerPatcher深度解析与实战 【免费下载链接】ExplorerPatcher This project aims to enhance the working environment on Windows 项目地址: https://gitcode.com/GitHub_Trending/ex/ExplorerPatcher 每次Wi…...
【性能评估】信标辅助双跳认知无线电无线中继选择方案的性能评估研究(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...
