设计模式(15)组合模式
一、介绍:
1、定义:组合多个对象形成树形结构以表示“整体-部分”的关系的层次结构。组合模式对叶子节点和容器节点的处理具有一致性,又称为整体-部分模式。
2、优缺点:
优点:
(1)高层模块调用简单:组合模式使得客户端代码可以一致地处理单个对象和组合对象,无须关心自己处理的是单个对象,还是组合对象,这简化了客户端代码。
(2)节点自由增加:更容易在组合体内加入新的对象,客户端不会因为加入了新的对象而更改源代码。
缺点:
(1)在使用组合模式时,其叶子和树枝的声明都是实现类,而不是接口,违反了依赖倒置原则。
(2)设计较复杂,客户端需要花更多时间理清类之间的层次关系。
(3)不容易限制容器中的构件。
3、组成:
(1)抽象构件(Component)角色:它的主要作用是为树叶构件和树枝构件声明公共接口,并实现它们的默认行为。在透明式的组合模式中抽象构件还声明访问和管理子类的接口;在安全式的组合模式中不声明访问和管理子类的接口,管理工作由树枝构件完成。(总的抽象类或接口,定义一些通用的方法,比如新增、删除)。
(2)树枝构件(Composite)角色 / 中间构件:是组合中的分支节点对象,它有子节点,用于继承和实现抽象构件。它的主要作用是存储和管理子部件,通常包含 Add()、Remove()、GetChild() 等方法。
(3)树叶构件(Leaf)角色:是组合中的叶节点对象,它没有子节点,用于继承或实现抽象构件。
// 定义抽象构件
public abstract class Component {protected String name;public Component(String name) {this.name = name;}public abstract void add(Component component);public abstract void remove(Component component);public abstract void display();
}// 定义叶子构件
public class Leaf extends Component {public Leaf(String name) {super(name);}@Overridepublic void add(Component component) {System.out.println("Cannot add to a leaf");}@Overridepublic void remove(Component component) {System.out.println("Cannot remove from a leaf");}@Overridepublic void display() {System.out.println("Leaf: " + name);}
}// 定义容器构件
public class Composite extends Component {private List<Component> children = new ArrayList<>();public Composite(String name) {super(name);}@Overridepublic void add(Component component) {children.add(component);}@Overridepublic void remove(Component component) {children.remove(component);}@Overridepublic void display() {System.out.println("Composite: " + name);for (Component component : children) {component.display();}}
}// 客户端代码
public class Client {public static void main(String[] args) {Component root = new Composite("root");Component leaf1 = new Leaf("leaf1");Component leaf2 = new Leaf("leaf2");Component composite1 = new Composite("composite1");Component leaf3 = new Leaf("leaf3");Component composite2 = new Composite("composite2");root.add(leaf1);root.add(leaf2);root.add(composite1);composite1.add(leaf3);composite1.add(composite2);root.display();}
}
4、应用场景:部分、整体场景,如树形菜单,文件、文件夹的管理。
二、demo:
1、菜单:
(1)数据库model
public class MenuDTO {private String menuName;private String menuCode;private String parentMenuCode;public MenuDTO(String menuName,String menuCode,String parentMenuCode){this.menuCode = menuCode;this.menuName = menuName;this.parentMenuCode = parentMenuCode;}/**省略所有set、get芳芳*/
}
抽象构件Component
public abstract class MenuComponent extends MenuDTO {MenuComponent(String menuName, String menuCode,String parentMenuCode) {super(menuName, menuCode,parentMenuCode);}void addMenu(MenuComponent component){}void removeMenu(MenuComponent component){}
}
(2)树枝构件(Composite):
public class MenuVO extends MenuComponent {private List<MenuComponent> children = new ArrayList<>();MenuVO(String menuName, String menuCode,String parentMenuCode) {super(menuName, menuCode,parentMenuCode);}@Overridevoid addMenu(MenuComponent component) {children.add(component);}@Overridevoid removeMenu(MenuComponent component) {}
}
(3)树叶
public class MenuLeaf extends MenuComponent {MenuLeaf(String menuName, String menuCode,String parentMenuCode) {super(menuName, menuCode,parentMenuCode);}@Overridevoid addMenu(MenuComponent component) {super.addMenu(component);}@Overridevoid removeMenu(MenuComponent component) {super.removeMenu(component);}
}
客户端:
public class Test {public static void main(String args[]) {MenuComponent menuVOS = listMenus();System.out.println(menuVOS);}public static MenuComponent listMenus(){//模拟数据库查询,查询所有一级菜单(menu_type = 1)、二级菜单(menu_type = 2)List<MenuDTO> firstMenus = new ArrayList<>();MenuDTO menuDTO = new MenuDTO("菜单1","cd1","root");firstMenus.add(menuDTO);menuDTO = new MenuDTO("菜单2","cd2","root");firstMenus.add(menuDTO);menuDTO = new MenuDTO("菜单3","cd3","root");firstMenus.add(menuDTO);List<MenuDTO> secondMenus = new ArrayList<>();menuDTO = new MenuDTO("菜单1-1","cd1-1","cd1");secondMenus.add(menuDTO);menuDTO = new MenuDTO("菜单1-2","cd1-2","cd1");secondMenus.add(menuDTO);menuDTO = new MenuDTO("菜单2-1","cd2-1","cd2");secondMenus.add(menuDTO);Map<String, List<MenuDTO>> childMenuMap = secondMenus.stream().collect(Collectors.groupingBy(MenuDTO::getParentMenuCode));/**实现* 根节点* 菜单1 菜单2 菜单3*菜单1-1 菜单1-2 菜单2-1* *///1、定义根节点MenuComponent root = new MenuVO("根节点","root",null);//2、处理菜单层级for(MenuDTO firstMenu : firstMenus){//二级菜单MenuComponent firstMenuVO = new MenuVO(firstMenu.getMenuName(),firstMenu.getMenuCode(),firstMenu.getParentMenuCode());//三级菜单List<MenuDTO> secondMenuVOs = childMenuMap.get(firstMenu.getMenuCode());if(!CollectionUtils.isEmpty(secondMenuVOs)){for(MenuDTO secondMenu : secondMenuVOs){MenuComponent secondMenuVO = new MenuVO(secondMenu.getMenuName(),secondMenu.getMenuCode(),secondMenu.getParentMenuCode());firstMenuVO.addMenu(secondMenuVO);}}root.addMenu(firstMenuVO);}return root;}
}
运行main方法
2、文件夹:
(1)抽象构件Component
public abstract class FileComponent {//文件名称protected String name;//文件的层级 1 一级目录 2 二级目录 ...protected Integer level;//文件的类型 1 文件夹 2文件protected Integer type;//添加子文件/文件夹public abstract void add(FileComponent fileComponent);//移除子文件/文件夹public abstract void remove(FileComponent fileComponent);//获取指定的子文件/文件夹public abstract FileComponent getChild(int index);//打印子 子文件/子文件夹 名称的方法public abstract void print();
}
(2)树枝构件(Composite)
public class FileFolder extends FileComponent{//文件夹可以有多个子文件夹或者子文件private List<FileComponent> fileComponentList;public FileFolder(String name, Integer level, Integer type) {this.name = name;this.level = level;this.type = type;this.fileComponentList = new ArrayList<>();}@Overridepublic void add(FileComponent fileComponent) {fileComponentList.add(fileComponent);}@Overridepublic void remove(FileComponent fileComponent) {fileComponentList.remove(fileComponent);}@Overridepublic FileComponent getChild(int index) {return fileComponentList.get(index);}@Overridepublic void print() {//打印菜单名称for (int i = 0; i < level; i++) {System.out.print("\t");}System.out.println(name);//打印子菜单或者子菜单项名称for (FileComponent component : fileComponentList) {component.print();}}
}
(3)树叶构件(Leaf)
public class FileItem extends FileComponent{public FileItem(String name, Integer level, Integer type) {this.name = name;this.level = level;this.type = type;}@Overridepublic void add(FileComponent fileComponent) {}@Overridepublic void remove(FileComponent fileComponent) {}@Overridepublic FileComponent getChild(int index) {return null;}@Overridepublic void print() {//打印文件的名称for (int i = 0; i < level; i++) {System.out.print("\t");}System.out.println(name);}
}
客户端:
public class Test {public static void main(String[] args) {//定义根目录FileComponent rootComponent = new FileFolder("我是根目录",1,1);//定义二级文件夹FileComponent secondLevelComponent = new FileFolder("我是二级目录",2,1);//定义文件FileComponent file = new FileItem("我是文件",3,2);//向根目录添加二级目录rootComponent.add(secondLevelComponent);//向二级目录添加文件secondLevelComponent.add(file);//打印rootComponent.print();}
}
相关文章:

设计模式(15)组合模式
一、介绍: 1、定义:组合多个对象形成树形结构以表示“整体-部分”的关系的层次结构。组合模式对叶子节点和容器节点的处理具有一致性,又称为整体-部分模式。 2、优缺点: 优点: (1)高层模块调…...

小黑子—spring:第一章
spring入门1.0 一 小黑子对spring基础进行概述1.1 spring导论1.2 传统Javaweb开发困惑及解决方法1.3 三大的思想提出1.3.1 IOC入门案例1.3.2 DI入门案例 1.4 框架概念1.5 初识spring1.5.1 Spring Framework 1.6 BeanFactory快速入门1.7 ApplicationContext快速入门1.8 BeanFact…...
【面向对象】理解面向对象编程中的封装性
封装性是面向对象编程中的重要概念之一,它允许开发者将数据和方法组合成一个独立的单元,并通过定义访问权限来控制对这个单元的访问。本文将深入探讨面向对象编程中的封装性,包括封装的概念、实现封装的方式以及封装的好处。通过详细的解释和…...
ES修改字段类型详解
一、需求概述 ES修改字段类型是指在已有的索引中,通过特定的操作方式将某个字段的类型修改为其它类型。当ES在建立索引的时候,已经确定好了每个字段的类型,而如果在建立后发现类型不符需求,就需要修改字段类型。 二、修改字段类…...

C++基础:函数模板
为了代码重用,代码必须是通用的;通用的代码就必须不受数据类型的限制。那么我们可以把数据类型改为一个设计参数,这种类型的程序设计称为参数化程序设计,软件模板有模板构造,包括函数模板和类模板。 函数模板可以用来…...

Facebook账号被封?那是因为没做对这些事
Facebook是全球最大的社交媒体平台之一,拥有数十亿的全球用户。它的主要产品包括Facebook(面向个人用户的社交媒体平台)、Instagram、WhatsApp和Messenger。同时他也是美国数字广告市场的主要参与者之一,其广告平台吸引了数百万广…...
虚拟机kafka
一、kafka安装 (1)解压 (2)修改名字为kafka212 (3)进入/opt/soft/kafka212/config目录,配置文件server.properties 21 broker.id0 36 advertised.listenersPLAINTEXT://192.168.91.11:9092 …...
软考 系统架构设计师系列知识点之设计模式(3)
接前一篇文章:软考 系统架构设计师系列知识点之设计模式(2) 所属章节: 老版(第一版)教材 第7章. 设计模式 第2节. 设计模式实例 2. 结构型模式 结构型模式控制了应用程序较大部分之间的关系。它将以不同…...
217. 存在重复元素、Leetcode的Python实现
博客主页:🏆看看是李XX还是李歘歘 🏆 🌺每天分享一些包括但不限于计算机基础、算法等相关的知识点🌺 💗点关注不迷路,总有一些📖知识点📖是你想要的💗 ⛽️今…...
49.Redis缓存设计与性能优化
缓存与数据库双写不一致小概率事件 //线程1 写数据库stock 5 ---------------》更新缓存 //线程2 写数据库stock 4 -----》更新缓存 //线程1 ------》写数据库stock 10 -----》删除缓存 //线程2 ---------------------------------------------------------------------…...

IDEA常用的一些插件
1、CodeGlance 代码迷你缩放图插件,可以快速拖动代码,和VScode一样 2、Codota 代码提示工具,扫描你的代码后,根据你的敲击完美提示。 Codota基于数百万个开源Java程序和您的上下文来完成代码行,从而帮助您以更少的…...

基于定容积法标准容器容积标定中的电动针阀自动化解决方案
摘要:在目前的六氟化硫气体精密计量中普遍采用重量法和定容法两种技术,本文分析了重量法中存在的问题以及定容法的优势,同时也指出定容法在实际应用中还存在自动化水平较低的问题。为了提高定容法精密计量过程中的自动化水平,本文…...

26 行为型模式-命令模式
1 命令模式介绍 2 命令模式原理 3 命令模式实现 模拟酒店后厨的出餐流程,来对命令模式进行一个演示,命令模式角色的角色与案例中角色的对应关系如下: 服务员: 即调用者角色,由她来发起命令. 厨师: 接收者,真正执行命令的对象. 订单: 命令中包含订单 /*** 订单类**/ public cl…...

一个Entity Framework Core的性能优化案例
概要 本文提供一个EF Core的优化案例,主要介绍一些EF Core常用的优化方法,以及在优化过程中,出现性能反复的时候的解决方法,并澄清一些对优化概念的误解,例如AsNoTracking并不包治百病。 本文使用的是Dotnet 6.0和EF…...
【Python 千题 —— 基础篇】列表排序
题目描述 题目描述 给定一个包含无序数字的列表,请将列表中的数字按从小到大的顺序排列,并输出排序后的列表。 输入描述 输入一个包含无序数字的列表。 输出描述 程序将对列表中的数字进行排序,并输出排序后的列表。 示例 示例 ① 1…...
leetcode26:删除有序数组中的重复项
leetcode26:删除有序数组中的重复项 方案一:依次遍历,如果不符合条件则冒泡交换到最后一个位置。o(n^2),结果超时 #include <algorithm> #include <iostream>using namespace std; class Solution { public:int removeDuplicat…...

[FSCTF 2023] web题解
文章目录 源码!启动!webshell是啥捏细狗2.0ez_php1Hello,youEZ_eval巴巴托斯! 源码!启动! 打开题目,发现右键被禁了 直接ctrlu查看源码得到flag webshell是啥捏 源码 <?php highlight_file(__FILE__); $😀&qu…...
linux查看内存的方式
1、显示内存状态:free -h 以合适的单位显示内存使用情况,最大为三位数,自动计算对应的单位值。单位有: B bytes K kilos M megas G gigas T teras $free -htotal used free shared buff/cache available Me…...

Python 编写 Flink 应用程序经验记录(Flink1.17.1)
目录 官方API文档 提交作业到集群运行 官方示例 环境 编写一个 Flink Python Table API 程序 执行一个 Flink Python Table API 程序 实例处理Kafka后入库到Mysql 下载依赖 flink-kafka jar 读取kafka数据 写入mysql数据 flink-mysql jar 官方API文档 https://nigh…...

如何 通过使用优先级提示,来控制所有网页资源加载顺序
当你打开浏览器的网络标签时,你会看到大量的活动。资源正在下载,信息正在提交,事件正在记录,等等。 由于有太多的活动,有效地管理这些流量的优先级变得至关重要。带宽争用是真实存在的,当所有请求同时触发时…...

接口测试中缓存处理策略
在接口测试中,缓存处理策略是一个关键环节,直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性,避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明: 一、缓存处理的核…...

微信小程序之bind和catch
这两个呢,都是绑定事件用的,具体使用有些小区别。 官方文档: 事件冒泡处理不同 bind:绑定的事件会向上冒泡,即触发当前组件的事件后,还会继续触发父组件的相同事件。例如,有一个子视图绑定了b…...
三维GIS开发cesium智慧地铁教程(5)Cesium相机控制
一、环境搭建 <script src"../cesium1.99/Build/Cesium/Cesium.js"></script> <link rel"stylesheet" href"../cesium1.99/Build/Cesium/Widgets/widgets.css"> 关键配置点: 路径验证:确保相对路径.…...

SpringCloudGateway 自定义局部过滤器
场景: 将所有请求转化为同一路径请求(方便穿网配置)在请求头内标识原来路径,然后在将请求分发给不同服务 AllToOneGatewayFilterFactory import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; impor…...

自然语言处理——循环神经网络
自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元(GRU)长短期记忆神经网络(LSTM)…...

优选算法第十二讲:队列 + 宽搜 优先级队列
优选算法第十二讲:队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...

C# 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...

推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材)
推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材) 这个项目能干嘛? 使用 gemini 2.0 的 api 和 google 其他的 api 来做衍生处理 简化和优化了文生图和图生图的行为(我的最主要) 并且有一些目标检测和切割(我用不到) 视频和 imagefx 因为没 a…...

DingDing机器人群消息推送
文章目录 1 新建机器人2 API文档说明3 代码编写 1 新建机器人 点击群设置 下滑到群管理的机器人,点击进入 添加机器人 选择自定义Webhook服务 点击添加 设置安全设置,详见说明文档 成功后,记录Webhook 2 API文档说明 点击设置说明 查看自…...

免费数学几何作图web平台
光锐软件免费数学工具,maths,数学制图,数学作图,几何作图,几何,AR开发,AR教育,增强现实,软件公司,XR,MR,VR,虚拟仿真,虚拟现实,混合现实,教育科技产品,职业模拟培训,高保真VR场景,结构互动课件,元宇宙http://xaglare.c…...