【设计模式-3.7】结构型——组合模式
说明:本文介绍结构型设计模式之一的组合模式
定义
组合模式(Composite Pattern)又叫作整体-部分(Part-Whole)模式,它的宗旨是通过将单个对象(叶子节点)和组合对象(树枝节点)用相同的接口进行表示,使得客户对单个对象和组合对象的使用具有一致性,属于结构型设计模式。(引自《设计模式就该这样学》P263)
文件系统
以文件系统为例,如下,是服务器上某个文件夹的文件结构,该文件夹下既有文件夹,也有文件
如果我要构建这样一个文件夹-文件系统,代码应该是这样写的,如下:
(图片文件,ImageFile)
/*** 图片文件*/
public class ImageFile {/*** 图片名称*/private String name;public ImageFile(String name) {this.name = name;}/*** 展示*/public void show(int space) {for (int i = 0; i < space; i++) {System.out.print(" ");}System.out.println(name);}
}
(电影文件,MovieFile)
/*** 电影文件*/
public class MovieFile {/*** 电影名称*/private String name;public MovieFile(String name) {this.name = name;}/*** 展示*/public void show(int space) {for (int i = 0; i < space; i++) {System.out.print(" ");}System.out.println(name);}
}
(文件夹,Folder,定义多个文件集合,并开放对应的增加方法)
import java.util.ArrayList;/*** 文件夹*/
public class Folder {/*** 文件夹名称*/private String name;/*** 文件夹下的文件夹*/private ArrayList<Folder> folders = new ArrayList<>();/*** 文件夹下的图片文件*/private ArrayList<ImageFile> imageFiles = new ArrayList<>();/*** 文件夹下的视频文件*/private ArrayList<MovieFile> movieFiles = new ArrayList<>();public Folder(String name) {this.name = name;}/*** 添加文件夹*/public void addFolder(Folder folder) {folders.add(folder);}/*** 添加图片文件*/public void addImageFile(ImageFile imageFile) {imageFiles.add(imageFile);}/*** 添加电影文件*/public void addMoveFile(MovieFile movieFile) {movieFiles.add(movieFile);}/*** 展示*/public void show(int space) {// 打印文件夹space++;for (int i = 0; i < space; i++) {System.out.print(" ");}System.out.println(name);for (Folder folder : folders) {folder.show(space);}// 打印图片文件space++;for (ImageFile imageFile : imageFiles) {imageFile.show(space);}// 打印电影文件space++;for (MovieFile movieFile : movieFiles) {movieFile.show(space);}}
}
(客户端使用,Client)
public class Client {public static void main(String[] args) {// 顶级文件夹Folder folder = new Folder("folder");// 二级文件夹Folder images = new Folder("images");Folder movies = new Folder("movies");// 三级目录下的文件ImageFile boy = new ImageFile("boy.png");ImageFile girl = new ImageFile("girl.png");images.addImageFile(boy);images.addImageFile(girl);// 三级文件夹Folder director1 = new Folder("heizeming");Folder director2 = new Folder("xiaolinzhengshu");// 四级目录MovieFile movieFile1 = new MovieFile("luoshengmen.mp4");MovieFile movieFile2 = new MovieFile("qiwushi.mp4");director1.addMoveFile(movieFile1);director1.addMoveFile(movieFile2);MovieFile movieFile3 = new MovieFile("duomingjian.mp4");MovieFile movieFile4 = new MovieFile("qiefu.mp4");director2.addMoveFile(movieFile3);director2.addMoveFile(movieFile4);movies.addFolder(director1);movies.addFolder(director2);// 文件夹添加到顶级文件夹folder.addFolder(images);folder.addFolder(movies);folder.show(0);}
}
运行,hora!(看!),能实现目的
但这里存在问题,文件系统,简单来说只有文件夹和文件两个实体对象,代码中的图片文件、电影文件可以抽象为文件(File),这是整体与个体的场景。
组合模式
使用组合模式改进上述代码,如下:
(抽象节点类,Node)
/*** 抽象节点*/
public abstract class Node {/*** 节点名称*/protected String name;public Node(String name) {this.name = name;}/*** 添加节点*/protected abstract void add(Node node);/*** 展示*/protected void show(int space) {for (int i = 0; i < space; i++) {System.out.print(" ");}System.out.println(name);}/*** 重载方法,使用的时候就不用给参数了*/protected void show() {show(0);}
}
(文件夹,Folder)
import java.util.ArrayList;/*** 文件夹*/
public class Folder extends Node {/*** 文件夹下的子节点*/private ArrayList<Node> childrenNodes = new ArrayList<>();/*** 调用父类的构造方法*/public Folder(String name) {super(name);}@Overrideprotected void add(Node node) {childrenNodes.add(node);}@Overrideprotected void show(int space) {super.show(space);space++;for (Node node : childrenNodes) {node.show(space);}}
}
(文件,File)
/*** 文件*/
public class File extends Node {/*** 调用父类的构造方法*/public File(String name) {super(name);}@Overrideprotected void add(Node node) {System.out.println("不能添加子节点");}@Overrideprotected void show(int space) {super.show(space);}
}
(客户端使用,Client)
public class Client {public static void main(String[] args) {// 顶级文件夹Folder folder = new Folder("folder");// 二级文件夹Folder images = new Folder("images");Folder movies = new Folder("movies");// 三级目录下的文件File boy = new File("boy.png");File girl = new File("girl.png");images.add(boy);images.add(girl);// 三级文件夹Folder director1 = new Folder("heizeming");Folder director2 = new Folder("xiaolinzhengshu");// 四级目录File movieFile1 = new File("luoshengmen.mp4");File movieFile2 = new File("qiwushi.mp4");director1.add(movieFile1);director1.add(movieFile2);File movieFile3 = new File("duomingjian.mp4");File movieFile4 = new File("qiefu.mp4");director2.add(movieFile3);director2.add(movieFile4);movies.add(director1);movies.add(director2);// 文件夹添加到顶级文件夹folder.add(images);folder.add(movies);folder.show();}
}
执行如下,也实现了目的
这么看下来,文件系统场景使用组合模式实现是很不错的,代码少了很多,也削减了文件夹类中的职责(可以对比下Folder类前后的代码)
使用场景
在《设计模式就该这样学》(P229)这本书中,提到状态模式适用于以下场景:
(1)希望客户端可以忽略组合对象与单个对象的差异;
(2)对象层次具备整体和部分,呈树形结构;
除了文件系统、企业组织架构场景,我还没想到其他使用场景;
总结
本文介绍了结构型设计模式中的组合模式,参考《设计模式就该这样学》、《秒懂设计模式》、《设计模式的艺术》(第一版)这三本书,其中的例子来自《秒懂设计模式》。
相关文章:

【设计模式-3.7】结构型——组合模式
说明:本文介绍结构型设计模式之一的组合模式 定义 组合模式(Composite Pattern)又叫作整体-部分(Part-Whole)模式,它的宗旨是通过将单个对象(叶子节点)和组合对象(树枝…...

Unity Mac 笔记本操作入门
在 macOS 笔记本电脑上使用 Unity Editor 的场景视图 (Scene View) 旋转视角,主要依赖于触摸板手势和键盘修饰键的组合。由于没有物理中键,操作方式会与 Windows 鼠标略有不同。 以下是具体的旋转视角操作: 1. 基本旋转视角 (Orbit) 这是最…...

实时数据仓库是什么?数据仓库设计怎么做?
目录 一、实时数据仓库是什么 (一)实时数据仓库的定义 (二)实时数据仓库的特点 二、实时数据仓库的应用场景 (一)金融行业 (二)电商行业 (三)物联网行…...

Linux(12)——基础IO(下)
目录 六、重定向 📄输出重定向 📄输入重定向 📄追加重定向 📄dup2 七、理解一切皆文件 八、缓冲区 🧠什么是缓冲区 🧠为什么要引入缓冲区 📄缓冲区类型 九、FILE 六、重定向 我们这…...

WPF可拖拽ListView
1.控件描述 WPF实现一个ListView控件Item子项可删除也可拖拽排序,效果如下图所示 2.实现代码 配合 WrapPanel 实现水平自动换行,并开启拖拽 <ListViewx:Name"listView"Grid.Row"1"Width"300"AllowDrop"True&…...
rocketmq索引
索引的理解 索引是什么, 索引实质是 相同数据的另一种存储结构 我们都知道读和写天然是存在矛盾的, 我们希望写的快,当然是顺序写的性能最高, 顺序写造成数据杂乱无章,没法按照一定的规律去找数。 如果想要找数的效率高, 必须要有结构组织的存放数据, 这样方便按规律找…...

[蓝桥杯]倍数问题
倍数问题 题目描述 众所周知,小葱同学擅长计算,尤其擅长计算一个数是否是另外一个数的倍数。但小葱只擅长两个数的情况,当有很多个数之后就会比较苦恼。现在小葱给了你 nn 个数,希望你从这 nn 个数中找到三个数,使得…...
定时任务的 cron 表达式
定时任务的 cron 表达式 一、什么时 cron 表达式 Cron表达式是一种广泛应用于Linux系统的时间表示格式,常用于定时任务的调度。Cron表达式可以通过指定不同的时间参数,描述一个在 未来某个时间点执行的任务。 二、Cron表达式语法 秒 分 时 日 月 周几…...

【MySQL】 约束
一、约束的定义 MySQL 约束是用于限制表中数据的规则,确保数据的 准确性 和 一致性 。约束可以在创建表时定义,也可以在表创建后通过修改表结构添加。 二、常见的约束类型 2.1 NOT NULL 非空约束 加了非空约束的列不能为 NULL 值,如果可以…...
MySQL 的 redo log 和 binlog 区别?
MySQL 的 redo log 和 binlog 区别? 1. 核心概念对比 1.1 redo log(重做日志) go专栏:https://duoke360.com/tutorial/path/golang 定位:InnoDB引擎层的物理日志作用:实现事务的持久性(ACID中的Durability)记录内容:物理页级别的修改(如"在page 5的offset 10…...

前端vue打开多个窗口,关闭窗口后才继续执行后续逻辑
1.打开第一个弹窗 弹窗的按钮代码 2.点击窗口1中按钮,打开新的窗口 // 请领单按钮点击 async cb_6_delClick() {let ls_yfbm this.st_3Value.BMBMlet pstring {}pstring.a ls_yfbmpstring.b this.queryFormDialog.outDepotDeptCodeawait this.openwithparm_w_md…...

「深度拆解」Spring Boot如何用DeepSeek重构MCP通信层?从线程模型到分布式推理的架构进化
什么是MCP? MCP(Model Context Protocol,模型上下文协议)是由Anthropic公司于2024年11月推出的开放标准协议,旨在为大型语言模型(LLM)与外部数据源、工具及系统提供统一的交互接口,被…...
如何避免在前端项目中出现重复的第三方依赖包?
在现代前端开发中,**重复的第三方依赖包(Duplicate Dependencies)**是导致项目体积膨胀、加载速度变慢、构建时间延长的常见问题。尤其在使用模块打包工具(如 Webpack、Vite、Rollup)时,若项目或其依赖的库…...
Java开发中复用公共SQL的方法
在一次Java后端开发的面试中,面试官问了我一个问题:“你在写代码时会复用公共SQL吗?如果会的话,能详细介绍一下你是如何实现的吗?”这个问题让我眼前一亮,因为在实际项目中,SQL复用确实是一个非…...

【西门子杯工业嵌入式-2-点亮一颗LED】
西门子杯工业嵌入式-2-点亮一颗LED 一、课程回顾与目标1.上节课内容回顾2.本节课目标 二、硬件连接与原理1. 硬件连接方式2. 连接实例 三、GPIO原理知识1. GPIO结构2. 推挽输出模式原理 四、软件实现步骤1. 项目结构设置2. 函数定义3. led.c 文件编写初始化函数 led_init交替闪…...

代码随想录算法训练营第60期第五十五天打卡
大家好,我们今天继续我们图论的部分,其实我们昨天是主要讲解了深搜与广搜的理论基础,我们大体上了解了两种算法的差异与适用情景,今天我们就继续我们的图论的章节,以后几天的题目是图论中比较有名的问题叫做岛屿问题&a…...

重磅更新! 基于Gemini 2.5 Pro打造的AI智能体PlantUML-X上线!
目录 图表绘制AI智能体PlantUML-X上线通过简单的提示词创建各种UML图:轻松搞定其它类型的技术图表: AI智能体PlantUML-X功能实测画一个在Java中的一个简单的用户登录功能的时序图效果展示:根据详细内容生成系统架构图效果展示:效果…...

[5-02-04].第01节:Jmeter环境搭建:
JMeter笔记大纲 Jmeter依赖于JDK,所以必须确保当前计算机上已经安装了JDK,并且配置了环境变量 一、JMeter概述: 1.1.JMeter是什么: JMeter是Appache组织使用java开发的一款测试工具 可以用于对服务器、网络或对象模拟巨大的负载…...

AI智能推荐实战之RunnableParallel并行链
导读:在现代AI应用开发中,如何高效处理多维度数据分析始终是开发者面临的核心挑战。当您需要同时进行情感分析、关键词提取和实体识别,或者要对比多个AI模型的输出结果时,传统的串行处理方式往往效率低下。 本文将深入解析LangCha…...
windows server2019 不成功的部署docker经历
由于现场网络限制,需要将docker 容器部署到windows-server 2019上 1.在windows server 2019上安装 docker-desktop,貌似内核版本太低,无法安装,g 然后曲线救国,window server 2019安装docker,折腾了半天,貌…...

Gemini开源项目DeepResearch:基于LangGraph的智能研究代理技术原理与实现
引言 在人工智能快速发展的今天,如何构建一个能够进行深度研究、自主学习和迭代优化的AI系统成为了技术前沿的重要课题。Gemini开源的DeepResearch一周收获7.9k Star,Google的开源项目Gemini DeepResearch技术通过结合LangGraph框架和Gemini大语言模型&…...
React状态管理Context API + useReducer
在 React 中,Context API useReducer 是一种轻量级的状态管理方案,适合中小型应用或需要跨组件共享复杂状态的场景。它避免了 Redux 的繁琐配置,同时提供了清晰的状态更新逻辑。 1. 基本使用步骤 (1) 定义 Reducer 类似于 Redux 的 reduce…...
【无标题】路径着色问题的革命性重构:拓扑色动力学模型下的超越与升华
路径着色问题的革命性重构:拓扑色动力学模型下的超越与升华 一、以色列路径着色模型的根本局限 mermaid graph TB A[以色列路径着色模型] --> B[强连通约束] A --> C[仅实边三角剖分] A --> D[静态色彩分配] B --> E[无法描述非相邻关系] C --> F[忽…...

Doris Catalog 联邦分析查询性能优化:从排查到优化的完整指南
在大数据分析中,Doris 的 Catalog 联邦分析功能为整合多源数据提供了有力支持。然而,在实际应用中,可能会遇到各种问题影响其正常运行。本文将详细剖析这些问题并提供解决方案。 一、联邦分析查询慢:内外表通用排查逻辑 当遇到 …...

01 Deep learning神经网络的编程基础 二分类--吴恩达
二分类 1. 核心定义 二分类任务是监督学习中最基础的问题类型,其目标是将样本划分为两个互斥类别。设样本特征空间为 X ⊆ R n \mathcal{X} \subseteq \mathbb{R}^n X⊆Rn,输出空间为 Y { 0 , 1 } \mathcal{Y} \{0,1\} Y{0,1},学习目标为…...

视频自动化分割方案:支持按时间与段数拆分
在日常视频处理任务中,如何快速将一个较长的视频文件按照指定规则拆分为多个片段,是许多用户都会遇到的问题。尤其对于需要批量处理视频的开发者、自媒体运营者或内容创作者来说,手动剪辑不仅效率低下,还容易出错。这是一款绿色免…...
Open SSL 3.0相关知识以及源码流程分析
Open SSL 3.0相关知识以及源码流程分析 编译 windows环境编译1、工具安装 安装安装perl脚本解释器、安装nasm汇编器(添加到环境变量)、Visual Studio编译工具 安装dmake ppm install dmake # 需要过墙2、开始编译 # 1、找到Visual Studio命令行编译工具目录 或者菜单栏直接…...

股指期货合约价值怎么算?
股指期货合约价值就是你买一手股指期货合约,理论上值多少钱。这个价值是根据期货的价格和合约乘数来计算的。就好比你买了一斤苹果,价格是5块钱一斤,那你买一斤就得付5块钱。股指期货也是一样,只不过它的计算稍微复杂一点点。 一…...

【QT】使用QT帮助手册找控件样式
选择帮助—》输入stylesheet(小写)—》选择stylesheet—》右侧选择Qt Style Sheets Reference 2.使用CtrlF—》输入要搜索的控件—》点击Customizing QScrollBar 3.显示参考样式表–》即可放入QT-designer的样式表中...

计算机网络(5)——数据链路层
1.概述 数据链路层负责一套链路上从一个节点向另一个物理链路直接相连的相邻节点传输数据报。换言之,主要解决相邻节点间的可靠数据传输 节点(nodes):路由器和主机 链路(links):连接相邻节点的通信信道 2.数据链路层服务 2.1 组帧 组帧(fra…...