设计模式——组合模式
什么是组合模式
组合模式(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…...

iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘
美国西海岸的夏天,再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至,这不仅是开发者的盛宴,更是全球数亿苹果用户翘首以盼的科技春晚。今年,苹果依旧为我们带来了全家桶式的系统更新,包括 iOS 26、iPadOS 26…...

stm32G473的flash模式是单bank还是双bank?
今天突然有人stm32G473的flash模式是单bank还是双bank?由于时间太久,我真忘记了。搜搜发现,还真有人和我一样。见下面的链接:https://shequ.stmicroelectronics.cn/forum.php?modviewthread&tid644563 根据STM32G4系列参考手…...

工业安全零事故的智能守护者:一体化AI智能安防平台
前言: 通过AI视觉技术,为船厂提供全面的安全监控解决方案,涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面,能够实现对应负责人反馈机制,并最终实现数据的统计报表。提升船厂…...

MFC内存泄露
1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...

关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案
问题描述:iview使用table 中type: "index",分页之后 ,索引还是从1开始,试过绑定后台返回数据的id, 这种方法可行,就是后台返回数据的每个页面id都不完全是按照从1开始的升序,因此百度了下,找到了…...
pam_env.so模块配置解析
在PAM(Pluggable Authentication Modules)配置中, /etc/pam.d/su 文件相关配置含义如下: 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块,负责验证用户身份&am…...

Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
大模型多显卡多服务器并行计算方法与实践指南
一、分布式训练概述 大规模语言模型的训练通常需要分布式计算技术,以解决单机资源不足的问题。分布式训练主要分为两种模式: 数据并行:将数据分片到不同设备,每个设备拥有完整的模型副本 模型并行:将模型分割到不同设备,每个设备处理部分模型计算 现代大模型训练通常结合…...

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

AI,如何重构理解、匹配与决策?
AI 时代,我们如何理解消费? 作者|王彬 封面|Unplash 人们通过信息理解世界。 曾几何时,PC 与移动互联网重塑了人们的购物路径:信息变得唾手可得,商品决策变得高度依赖内容。 但 AI 时代的来…...