设计模式(结构型)-组合模式
定义
组合模式的定义为:将对象组合成树形结构以表示 “部分 - 整体” 的层次结构,并且使得用户对单个对象和组合对象的使用具有一致性。其最关键的实现要点在于,简单对象和复合对象必须实现相同的接口,这一特性正是组合模式能够对组合对象和简单对象进行统一处理的基石。
想象一下,在一个文件系统中,文件是简单对象,文件夹是复合对象,文件夹可以包含文件和其他子文件夹。通过组合模式,我们可以将文件和文件夹看作是具有相同接口的对象,无论是对单个文件进行操作,还是遍历整个文件夹及其子文件夹,操作方式都能保持一致,极大地简化了代码逻辑。
类图
角色
-
抽象构件(Component)角色:抽象构件是一个抽象角色,它为参加组合的对象定义了公共接口以及默认行为。在透明式的组合模式中,它还负责管理所有的子对象;而在安全式的组合模式里,管理子对象的方法由树枝结构对象定义。抽象构件就像是整个层次结构的 “标准制定者”,确保所有对象都遵循统一的规则。
-
树叶构件(Leaf)角色:树叶对象是没有下级子对象的对象,它们定义了参加组合的原始对象的行为。比如在上述文件系统例子中,具体的文件就是树叶构件,它们实现了抽象构件定义的接口,执行如读取、写入等具体操作。
-
树枝构件(Composite)角色:树枝对象代表有下级子对象的对象,它们给出所有管理子对象的方法实现,如添加(Add)、删除(Remove)等。文件夹在文件系统中就扮演着树枝构件的角色,通过这些方法,文件夹可以方便地管理其包含的文件和子文件夹。
优缺点
优点
-
统一处理对象与对象容器:组合模式让客户端代码能够以相同的方式处理单个对象和对象容器,无需关心具体操作的是简单对象还是复杂的组合对象,极大地提高了代码的简洁性和可读性。
-
解耦客户代码与对象容器结构:将客户端代码与复杂的对象容器结构分离,降低了系统的耦合度,使得代码更易于维护和扩展。当对象容器结构发生变化时,客户端代码无需进行大量修改。
-
便于添加新构件:由于组合模式的统一接口设计,往组合对象中添加新的构件变得更加容易,不会对现有代码造成较大影响,增强了系统的可扩展性。
缺点
-
设计复杂度增加:组合模式的引入使得设计变得更加复杂,客户端需要花费更多时间去理清类之间的层次关系,理解各个角色的职责和交互方式。
-
接口设计挑战:在透明组合模式中,可能会导致叶子节点获得一些不属于它的方法,违背接口隔离性原则;而安全组合模式虽然避免了这一问题,但也可能增加客户端代码的判断逻辑。
注意事项
在使用组合模式时,有一些关键要点需要注意。当系统需要频繁遍历树枝结构的子构件时,可以考虑在父构件中存储遍历子构件的结构作为缓存,以提高系统性能。同时,客户端应尽量避免直接调用树叶类中的方法,而是借助父类的多态性完成调用,这样可以增强代码的复用性,减少代码冗余。
组合模式的类型
透明组合模式
透明组合模式将组合对象所有的公共方法都定义在抽象组件内。这种方式的优势在于客户端无需区分当前对象是树枝节点还是叶子节点,因为它们拥有完全一致的接口。然而,其缺点也很明显,叶子节点会得到一些不属于它的方法,破坏了接口隔离性原则。
安全组合模式
安全组合模式仅规定了系统各个层次最基础的一致性行为,把组合(树节点)自身的方法,如树枝节点管理子类的方法,放到自身当中。这种模式避免了透明组合模式中叶子节点获得多余方法的问题,但客户端在使用时可能需要更多地关注对象的类型,以调用合适的方法。
使用场景
-
当需要表示一个对象整体或部分的层次结构时,组合模式是绝佳选择,如组织结构图、文件目录结构等。
-
若希望用户忽略组合对象与单个对象的差异,统一使用组合结构中的所有对象,组合模式能够满足这一需求,简化用户操作。
使用案例
-
HashMap:HashMap 中的 putAll 方法,其参数是一个 Map。这里将一个 Map(可能是简单的键值对集合,也可能是包含多个子 Map 的复杂结构)作为整体添加到另一个 Map 中,体现了组合模式的思想,将单个对象和组合对象进行统一处理。
-
MyBatis 中的 SqlNode 接口:在 MyBatis 中,SqlNode 接口是一个抽象构件,具体的 SqlNode 实现类如 TextSqlNode(树叶构件)和 DynamicSqlNode(树枝构件),通过组合模式构建出复杂的 SQL 解析结构,实现对 SQL 语句的灵活处理和解析。
代码实现
-
抽象构件(
OrganizationComponent
):定义了组织组件的公共接口,包含一个抽象方法showInfo
用于显示信息,同时提供了add
和remove
方法的默认实现,在树叶构件中调用这些方法会抛出不支持操作的异常。
// 抽象构件角色
abstract class OrganizationComponent {protected String name;public OrganizationComponent(String name) {this.name = name;}// 抽象方法,用于显示信息public abstract void showInfo();// 以下两个方法在安全组合模式中,只在树枝构件中实现public void add(OrganizationComponent component) {throw new UnsupportedOperationException("不支持此操作");}public void remove(OrganizationComponent component) {throw new UnsupportedOperationException("不支持此操作");}
}
-
树枝构件(
Department
):继承自OrganizationComponent
,表示部门,包含一个List
用于存储子组件(员工或子部门),实现了add
、remove
和showInfo
方法,showInfo
方法会递归调用子组件的showInfo
方法。
// 树枝构件角色:部门
import java.util.ArrayList;
import java.util.List;class Department extends OrganizationComponent {private List<OrganizationComponent> children = new ArrayList<>();public Department(String name) {super(name);}@Overridepublic void add(OrganizationComponent component) {children.add(component);}@Overridepublic void remove(OrganizationComponent component) {children.remove(component);}@Overridepublic void showInfo() {System.out.println("部门: " + name);for (OrganizationComponent component : children) {component.showInfo();}}
}
-
树叶构件(
Employee
):继承自OrganizationComponent
,表示员工,实现了showInfo
方法,用于显示员工信息。
// 树叶构件角色:员工
class Employee extends OrganizationComponent {public Employee(String name) {super(name);}@Overridepublic void showInfo() {System.out.println("员工: " + name);}
}
-
测试(
Main
):创建了部门和员工对象,组装了组织架构,并调用根部门的showInfo
方法显示整个组织架构的信息。
// 主类,用于测试
public class Main {public static void main(String[] args) {// 创建部门Department rootDepartment = new Department("总公司");Department subDepartment1 = new Department("研发部");Department subDepartment2 = new Department("市场部");// 创建员工Employee employee1 = new Employee("张三");Employee employee2 = new Employee("李四");Employee employee3 = new Employee("王五");// 组装组织架构subDepartment1.add(employee1);subDepartment2.add(employee2);subDepartment2.add(employee3);rootDepartment.add(subDepartment1);rootDepartment.add(subDepartment2);// 显示组织架构信息rootDepartment.showInfo();}
}
总结
组合模式以其独特的树形结构设计和统一接口的处理方式,为处理对象的 “部分 - 整体” 关系提供了高效的解决方案。虽然它存在一定的设计复杂性,但在合适的场景下,能够显著提升代码的可维护性、扩展性和复用性,是软件开发中不可或缺的重要设计模式之一。
相关文章:
设计模式(结构型)-组合模式
定义 组合模式的定义为:将对象组合成树形结构以表示 “部分 - 整体” 的层次结构,并且使得用户对单个对象和组合对象的使用具有一致性。其最关键的实现要点在于,简单对象和复合对象必须实现相同的接口,这一特性正是组合模式能够对…...
使用 IDEA + Maven 搭建传统 Spring MVC + Thymeleaf 项目的详细步骤
使用 IDEA Maven 搭建传统 Spring MVC Thymeleaf 项目 环境准备步骤 1:创建 Maven 项目步骤 2:添加依赖(pom.xml)步骤 3:配置 web.xml步骤 4:Spring 配置类(Java Config)步骤 5&am…...
「Mac畅玩AIGC与多模态19」开发篇15 - 判断节点与工具节点联动示例
一、概述 本篇在引入工具节点的基础上,进一步结合判断节点(条件分支),实现根据用户输入内容动态控制是否调用外部接口。通过构建“用户是否需要天气信息”的条件逻辑,开发人员将掌握如何在 Dify 工作流中通过条件判断…...
docker 外部能访问外网,内部不行(代理问题)
如果宿主机访问外网依赖代理(比如 http_proxy 环境变量),容器默认不会继承。需要显式传入代理: docker run -e http_proxy... -e https_proxy... ...在 docker-compose 中配置 HTTP/HTTPS 代理 version: 3 services:app:image: …...
模糊控制理论(含仿真)
本文讲解模糊控制理论、设计步骤以及案例。 1. 模糊控制原理: 模糊控制(Fuzzy Control)是一种基于模糊逻辑推理的人类经验规则实现的控制方法,适用于对系统模型不精确或难以建立精确数学模型的复杂系统。它利用“如果…那么…”&…...
《 C++ 点滴漫谈: 三十六 》lambda表达式
一、引言 在 C98 和 C03 时代,尽管 C 拥有强大的泛型编程能力和丰富的面向对象特性,但在表达局部逻辑、回调行为或一次性函数处理时,程序员却常常需要冗长的代码来定义函数对象(functor),或者使用函数指针…...
【C/C++】函数模板
🎯 C 学习笔记:函数模板(Function Template) 本文是面向 C 初学者的函数模板学习笔记,内容包括基本概念、定义与使用、实例化过程、注意事项等,附带示例代码,便于理解与复现。 📌 一…...
电赛经验分享——模块篇
1、前言 打算在这一个专栏中,分享一些本科控制题电赛期间的经验,和大家共同探讨,也希望能帮助刚刚参加电赛的同学,了解一些基本的知识。一些见解和看法可能不同或有错误,欢迎批评指正。 在本文中,主要介绍笔…...
LeetCode 热题 100 70. 爬楼梯
LeetCode 热题 100 | 70. 爬楼梯 大家好,今天我们来解决一道经典的动态规划入门题——爬楼梯。这道题在LeetCode上被标记为简单难度,要求我们计算爬到第n阶楼梯的不同方法数,每次可以爬1或2个台阶。下面我将详细讲解解题思路,并附…...
浔川AI测试版内测报告
浔川AI测试版内测报告 一、引言 本次对浔川AI测试版进行内测,旨在全面评估其功能表现与性能状况,为后续的优化升级及正式上线提供有力依据。 二、测试环境 1. 硬件环境:[Windows 10】 2. 软件环境:操作系统为【核桃编程]ÿ…...
Leetcode刷题记录31——旋转图像
题源:https://leetcode.cn/problems/rotate-image/description/?envTypestudy-plan-v2&envIdtop-100-liked 题目描述: 思路一: 💡 解题思路:分两步完成旋转 虽然“直接旋转”看起来有点抽象,但我们…...
攻防世界-php伪协议和文件包含
fileinclude 可以看到正常回显里面显示lan参数有cookie值表示为language 然后进行一个判断,如果参数不是等于英语,就加上.php,那我们就可以在前面进行注入一个参数,即flag, payload:COOKIE:languageflag …...
[C++] 小游戏 决战苍穹
大家好,各位看到这个标题,斗破苍穹什么时候改叫决战苍穹了?其实,因为版权等一系列问题,斗破苍穹正式改名为决战苍穹,这个版本主要更新内容为解决了皇冠竞技场太过影响游戏平衡,并且提高了一些装…...
项目成本管理_挣得进度ES
在项目成本管理的新实践中, 通过挣值管理(EVM) 的扩展,引入 挣得进度ES 这一概念, ES是EVM理论和实践的延伸,挣得进度理论用ES和实际时间(AT) 替代了传统EVM所使用的进度偏差测量指标SV(挣值—计划价值)。 使用这种替代方法计算…...
矩阵快速幂 快速求解递推公式
文章目录 习题790.多米诺和托米诺平铺 对于一个给定的递推公式,例如dp[i] dp[i-1] * a dp[i-2] * b,那么常用的做法,肯定是使用o(n)的时间复杂度进行线性求解,但是如果 n 10 18 n{10}^{18} n1018,那么肯定超时的,这…...
驱动开发硬核特训 · Day 28(上篇):pinctrl 子系统详解与实战分析
📅 日期:2025-05-05 📚 技术平台:嵌入式Jerry(B站) 一、引言 在嵌入式系统中,SoC 芯片的引脚通常具有多种功能,如 GPIO、UART、I2C、SPI 等。为了在不同的应用场景中灵活配置引脚功…...
20250505下载VLC for Android
20250505下载VLC for Android 2025/5/5 14:35 缘起:做Rockchip的RK3566的Android13下的跨网段PING。 酷芯的图传网段 和 softAP/以太网RJ45共享网段之间互相PING通。 图传的原厂/供应商说可以使用ffmpeg进行rtsp流的转发。 后来确认VLC for Android版本只有接受流&a…...
Jetpack Compose 响应式布局实战:BoxWithConstraints 完全指南
深入理解 Jetpack Compose 中的 BoxWithConstraints 前言 在构建现代 Android 应用时,响应式设计已成为必不可少的要求。Jetpack Compose 作为 Android 的现代 UI 工具包,提供了 BoxWithConstraints 这一强大组件,帮助我们轻松创建能够适应…...
ZYNQ笔记(十七):IP核封装与接口定义
版本:Vivado2020.2(Vitis) 任务:将“HDMI彩条显示实验”(正点原子 ZYNQ FPGA 开发视频)中所实现的 RGB2DVI 模块封装成一个 IP 核。 目录 一、介绍 (1)IP核 (2&#x…...
学习笔记msp430f5529lp
注:本文仅用于个人学习使用,记录笔记。 学习视频msp430f5529库函数入门教程 00.序言_哔哩哔哩_bilibili 向大佬致敬理工男小帅-CSDN博客 CCS环境快捷键使用 代码注释:Ctrl/ 提示/补全: CtrlShiftC 放大:Ctrl 缩小:Ctrl- 切换选择模式&…...
人工智能应用:从技术突破到生态重构的演进之路
一、人工智能的发展历程:从符号主义到通用智能探索 人工智能(AI)的发展始于20世纪中叶,其历程可划分为四个关键阶段: 符号主义与早期探索(1950s-1970s) 以逻辑推理和专家系统为核心&…...
【ZYNQ Linux移植】4-内核移植
文章目录 0 写在前面1 内核源码的文件结构2 Linux内核移植2.1 移植配置文件2.2 移植设备树2.3 创建脚本进行编译2.4 备份相关文件 3 测试4 总结5 参考资料 0 写在前面 这是一个系列博客,详细介绍如何在 ZYNQ 与 ZYNQ MP 平台上如何移植 Linux 系统。目前网络上的大部…...
代码随想录算法训练营第三十二天
LeetCode/卡码网题目: 518. 零钱兑换 II377. 组合总和 Ⅳ790. 多米诺和托米诺平铺(每日一题)57. 爬楼梯(第八期模拟笔试) 其他: 今日总结 往期打卡 背包问题特点: 滚动数组背包遍历顺序 完全背包从小到大,即基于当前物品更新过的继续更新01背包从大到…...
java CompletableFuture 异步编程工具用法1
1、测试异步调用: static void testCompletableFuture1() throws ExecutionException, InterruptedException {// 1、无返回值的异步任务。异步线程执行RunnableCompletableFuture.runAsync(() -> System.out.println("only you"));// 2、有返回值的异…...
Spring Boot 集成 Solr 的详细步骤及示例
环境准备 安装 Solr :从 Solr 官网(Welcome to Apache Solr - Apache Solr)下载并安装最新版本,然后通过命令 bin/solr start 启动 Solr 服务,使用 bin/solr create -c mycore 创建一个新的 Solr 核心。 安装 JDK &am…...
Nemotron-Research-Tool-N1 如何提升大语言模型工具使用能力?
Nemotron-Research-Tool-N1如何提升大语言模型工具使用能力? 如今,大语言模型(LLMs)发展迅猛,给它配备外部工具成为研究热点。但传统方法存在不少问题。这篇论文提出的Nemotron-Research-Tool-N1系列模型带来新突破&a…...
OpenCV进阶操作:图像直方图、直方图均衡化
文章目录 一、图像直方图二、图像直方图的作用三、使用matplotlib方法绘制直方图2.使用opencv的方法绘制直方图(划分16个小的子亮度区间)3、绘制彩色图像的直方图 四、直方图均衡化1、绘制原图的直方图2、绘制经过直方图均衡化后的图片的直方图3、自适应…...
Android控件VideoView用法
一 控件UI <VideoViewandroid:id="@+id/videoView"android:layout_width="match_parent"android:layout_height="match_parent"android:scaleType="fitCenter" /> 二 配置 <?xml version="1.0" encoding="u…...
人工智能数学基础(十)—— 图论
图论作为数学的重要分支,为人工智能提供了强大的建模和分析工具。无论是社交网络分析、路径规划还是数据结构设计,图论都发挥着不可替代的作用。今天,我将带领大家深入浅出地探索图论的核心概念,并结合 Python 实例,让…...
深入探索Anthropic Claude与Spring AI的融合应用
深入探索Anthropic Claude与Spring AI的融合应用 前言 在人工智能的蓬勃发展进程中,自然语言处理领域不断涌现出强大的模型和工具。Anthropic Claude系列基础AI模型凭借其出色的性能,在各种应用场景中展现出巨大潜力,为开发者和企业提供了丰…...
Python爬虫实战:获取优美图库各类高清图片,为用户提供设计素材
一、引言 在互联网时代,高清壁纸资源丰富多样,而优美图库作为一个提供大量精美壁纸的网站,吸引了众多用户。通过 Python 爬虫技术,可以自动化地从该网站获取所需的壁纸资源,为用户节省时间和精力。然而,网站通常会采取反爬措施来防止数据被恶意抓取,因此需要在爬虫程序…...
Java常用注解大全(基于JDK17+SpringBoot3)
一、基础注解(Java原生) 编译相关 @Override:方法重写校验 java 复制 下载 @Override public String toString() { return "CustomObj"; } @Deprecated:标记过时元素 java 复制 下载 @Deprecated(since="1.8", forRemoval=true) public void oldMethod…...
【NLP】30. 深入理解 In-Context Learning 的核心机制与策略
In-Context Learning(ICL)详解:提示学习时代的语言理解 一、什么是 In-Context Learning(ICL)? In-Context Learning 是指: 不改变模型参数,通过在输入中加入示例(demon…...
数字化工厂中央控制室驾驶舱系统 - Windows 部署笔记
数字化工厂中央控制室驾驶舱系统 - Windows 部署笔记 环境准备 这篇笔记记录了我在 Windows 10/11 上部署数字化工厂中央控制室驾驶舱系统的全过程,包括各种常见问题的解决方法。部署过程中使用了国内镜像源来加快下载速度。 前置需求 Python:3.8 到…...
数据库的原子事务
原子事务 11.1 全有或全无效应 二级索引需要原子性的多键更新,这不仅对数据库内部一致性至关重要,也对应用数据的一致性非常有用(例如考虑账户余额和账户交易)。 我们将放弃get-set-del接口,并添加一个新的接口来允…...
基于51单片机的红外人体感应报警器
基于51单片机的人体监测报警 (仿真+程序+原理图+PCB) 功能介绍 具体功能: 1.按下报警按钮会发生红LED蜂鸣器声光报警; 2.若检测到人,黄LED打开; 3.按下布防按键&…...
从Excel到高级工具:数据分析进阶指南
从Excel到高级工具:数据分析进阶指南 在数据分析的世界里,Excel曾经是众多人的第一站。它简单、直观、功能强大,从普通用户到专业人士,无不对其依赖。然而,随着数据规模增长、分析需求升级,Excel渐渐显得力…...
Excel VBA 自定义函数
一、VBA 函数基础概念 在 Excel VBA 中,函数主要分为两种类型: Sub 过程:执行操作但不返回值Function 函数:执行操作并返回结果 基本语法示例 1. Function 函数示例 定义一个返回字符串的公共函数 Public Function GetGreetin…...
004-nlohmann/json 快速认识-C++开源库108杰
了解 nlohmann/json 的特点;理解编程中 “数据战场”划分的概念;迅速上手多种方式构建一个JSON对象; 1 特点与安装 nlohmann/json 是一个在 github 长期霸占 “JSON” 热搜版第1的CJSON处理库。它的最大优点是与 C 标准库的容器数据…...
【Quest开发】接入语音转文字
参考官方文档:https://developers.meta.com/horizon/documentation/unity/voice-sdk-tutorials-overview 软件:Unity 2022.3.51f1c1、vscode、Meta XR All in One SDK V72 硬件:Meta Quest3 注意:需全程科学上网 Meta提供了一…...
Vim 命令从头学习记录
学习链接:eleon-vim基础教程 Vim - 基础翻屏操作 光标移动:hjkl 20j 向下移动20行,w 向后移动一个字符,b 向前移动一个字符。 Ctrl u 向上翻半页 UP Ctrl d 向下翻半页 Down Ctrl f 向下翻整页 Forward Ctrl b 向上翻整页 …...
[Linux]物理地址到虚拟地址的转化
[Linux]物理地址到虚拟地址的转化 水墨不写bug 文章目录 一、再次认识地址空间二、页表1、页表的结构设计2、页表节省了空间,省在哪里?3、页表的物理实现 一、再次认识地址空间 OS和磁盘交互的内存基本单位是4KB,这4KB通常被称为内存块。OS对…...
js获取明天日期、Vue3大菠萝 Pinia的使用
直接上代码 const today new Date(2019, 2, 28) const finalDate new Date(today) finalDate.setDate(today.getDate() 3)console.log(finalDate) // 31 March 2019 安装 yarn add pinia # or with npm npm install pinia创建第一个store仓库 1、在src目录下创建store目录…...
矩阵置零(中等)
可以用两个标记数组分别记录每一行和每一列是否有零出现。 首先遍历该数组一次,如果某个元素为 0,那么就将该元素所在的行和列所对应标记数组的位置置为 true。然后再次遍历该数组,用标记数组更新原数组。 class Solution {public void set…...
GZ人博会自然资源系统(测绘)备考笔记
本文为备考 GZ人才博览会自然资源系统(测绘) 的笔记,包括若干 知识点整理 及 近两年考核(面试)真题 (文末附《GZ人博会自然资源系统(测绘)备考笔记》1 的下载链接)。 目录…...
《进制转换的终极指南:原理、方法与编程应用》
🚀个人主页:BabyZZの秘密日记 📖收入专栏:C语言 🌍文章目入 一、进制转换的基本原理二、进制转换方法总结(一)使用权重法的转换1. 二进制 → 十进制2. 八进制 → 十进制3. 十六进制 → 十进制 &…...
2025系统架构师---论软件的设计模式论文
2023 年,我所在的公司承担了某部网络靶场的研发任务。我作为公司的技 术总监,希望能打造基于网络靶场的系列产品,参与到项目的设计中,以期开发 扩展性和可维护性良好的网络靶场,为以后的产品开发打下基础。网络靶场是网 络安全技术研究的基础支撑平台,它利用虚拟的和实物…...
嵌入式Linux驱动学习
Ubuntu18 下载链接 https://releases.ubuntu.com/bionic/ Ubuntu配置静态IP 更新Ubuntu18的镜像源 以清华大学镜像源举例 网站:https://mirrors.tuna.tsinghua.edu.cn/ 第一步点开网站搜索ubuntu然后点击问号 第二步选择自己的Ubuntu版本 第三步在Ubuntu中复制…...
基于大模型的子宫腺肌病全流程预测与诊疗方案研究报告
目录 一、引言 1.1 研究背景与意义 1.2 研究目的与创新点 二、子宫腺肌病概述 2.1 疾病定义与病理机制 2.2 流行病学特征 2.3 现有诊断与治疗方法综述 三、大模型技术原理与应用基础 3.1 大模型简介 3.2 在医疗领域的应用现状 3.3 适用于子宫腺肌病预测的可行性分析…...
Notebook.ai 开源程序是一套工具,供作家、游戏设计师和角色扮演者创建宏伟的宇宙 - 以及其中的一切
一、软件介绍 文末提供程序和源码下载 Notebook.ai 开源程序是一套工具,供作家、游戏设计师和角色扮演者创建宏伟的宇宙 - 以及其中的一切。 二、软件特点 Notebook 是作家的规划工具,用于创建从宇宙到角色、情节到单个项目的任何内容。通过浏览器、…...