组合模式(Composite Pattern)
使用组合模式(Composite Pattern)是一个更优雅的方式来表示菜单和菜单项。组合模式允许我们将单个对象(如菜单项)和组合对象(如菜单)以相同的方式处理。
解决方案:
- 创建组合结构:我们将菜单项和菜单抽象为
MenuComponent,其中菜单项是叶节点,菜单是组合节点。 Menu类:可以包含子菜单或菜单项。MenuItem类:代表具体的菜单项。Waitress类:只需处理一个MenuComponent对象,并能够遍历所有的菜单和菜单项。
代码实现
1. 创建抽象类 MenuComponent
import java.util.Iterator;public abstract class MenuComponent {public void add(MenuComponent menuComponent) {throw new UnsupportedOperationException();}public void remove(MenuComponent menuComponent) {throw new UnsupportedOperationException();}public MenuComponent getChild(int i) {throw new UnsupportedOperationException();}public String getName() {throw new UnsupportedOperationException();}public String getDescription() {throw new UnsupportedOperationException();}public double getPrice() {throw new UnsupportedOperationException();}public void print() {throw new UnsupportedOperationException();}public Iterator<MenuComponent> createIterator() {throw new UnsupportedOperationException();}
}
2. 实现 MenuItem 类(叶节点)
public class MenuItem extends MenuComponent {private String name;private String description;private double price;public MenuItem(String name, String description, double price) {this.name = name;this.description = description;this.price = price;}@Overridepublic String getName() {return name;}@Overridepublic String getDescription() {return description;}@Overridepublic double getPrice() {return price;}@Overridepublic void print() {System.out.println(" " + getName() + ": " + getDescription() + " -- ¥" + getPrice());}
}
3. 实现 Menu 类(组合节点)
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;public class Menu extends MenuComponent {private List<MenuComponent> menuComponents = new ArrayList<>();private String name;private String description;public Menu(String name, String description) {this.name = name;this.description = description;}@Overridepublic void add(MenuComponent menuComponent) {menuComponents.add(menuComponent);}@Overridepublic void remove(MenuComponent menuComponent) {menuComponents.remove(menuComponent);}@Overridepublic MenuComponent getChild(int i) {return menuComponents.get(i);}@Overridepublic String getName() {return name;}@Overridepublic String getDescription() {return description;}@Overridepublic void print() {System.out.println("\n" + getName() + ": " + getDescription());System.out.println("---------------------");for (MenuComponent menuComponent : menuComponents) {menuComponent.print();}}
}
4. 实现 Waitress 类
public class Waitress {private MenuComponent allMenus;public Waitress(MenuComponent allMenus) {this.allMenus = allMenus;}public void printMenu() {allMenus.print();}
}
5. 修改测试代码
public class CompositePatternDemo {public static void main(String[] args) {MenuComponent breakfast = new Menu("早餐菜单", "早餐6:00~9:00");MenuComponent lunch = new Menu("午餐菜单", "午餐11:30~14:30");MenuComponent coffeeMenu = new Menu("咖啡菜单", "全天24小时供应");MenuComponent allMenus = new Menu("所有菜单", "所有可使用的菜单");// Add breakfast itemsbreakfast.add(new MenuItem("胡辣汤", "素胡辣汤", 3));breakfast.add(new MenuItem("油条", "按斤称", 2));breakfast.add(new MenuItem("包子", " 莲藕馅", 1.5));// Add lunch itemslunch.add(new MenuItem("茄汁面", "番茄鸡蛋汤面", 8));lunch.add(new MenuItem("蒜汁面", "凉拌面", 7));lunch.add(new MenuItem("臊子面", "羊肉臊子面", 15));// Add coffee itemscoffeeMenu.add(new MenuItem("生椰拿铁", "少冰", 9.99));coffeeMenu.add(new MenuItem("丝绒拿铁", "热饮", 12.99));coffeeMenu.add(new MenuItem("茉莉生椰拿铁", "外卖,少冰,不另外加糖", 16.99));// Combine menusallMenus.add(breakfast);allMenus.add(lunch);allMenus.add(coffeeMenu);// Create waitress and print all menusWaitress waitress = new Waitress(allMenus);waitress.printMenu();}
}
输出结果
所有菜单: 所有可使用的菜单
---------------------早餐菜单: 早餐6:00~9:00
---------------------胡辣汤: 素胡辣汤 -- ¥3.0油条: 按斤称 -- ¥2.0包子: 莲藕馅 -- ¥1.5午餐菜单: 午餐11:30~14:30
---------------------茄汁面: 番茄鸡蛋汤面 -- ¥8.0蒜汁面: 凉拌面 -- ¥7.0臊子面: 羊肉臊子面 -- ¥15.0咖啡菜单: 全天24小时供应
---------------------生椰拿铁: 少冰 -- ¥9.99丝绒拿铁: 热饮 -- ¥12.99茉莉生椰拿铁: 外卖,少冰,不另外加糖 -- ¥16.99
代码解释
MenuComponent抽象类:定义了菜单和菜单项的通用操作,如add()、remove()、getChild()等。组合对象(Menu)可以包含MenuComponent,而叶节点(MenuItem)则只处理具体的菜单项。Menu类:可以包含子菜单或菜单项,通过组合来管理多个菜单和子菜单。MenuItem类:叶节点,表示具体的菜单项。Waitress类:只需要处理一个MenuComponent对象,无论是叶节点(菜单项)还是组合对象(菜单),都可以通过相同的方式处理并打印。
总结
通过使用组合模式,Menu 和 MenuItem 都被视为 MenuComponent,Waitress 只需要一个 MenuComponent 对象即可遍历所有菜单和菜单项。这使得代码非常灵活,易于扩展和维护。
组合模式 (Composite Pattern) 详细解释
1. 组合模式的概念
组合模式允许我们将对象组合成树形结构来表示“部分-整体”的层次结构。它使得客户端能够以一致的方式处理单个对象和组合对象。
在我们的场景中,菜单项(MenuItem)是基本的元素,菜单(Menu)则是组合对象,可以包含其他菜单或菜单项。Waitress 类不需要知道它是在处理菜单还是菜单项,只要调用通用接口即可,这正是组合模式的强大之处。
2. 类结构概述
-
MenuComponent:抽象基类,提供了所有菜单和菜单项的通用操作。它定义了组合和叶节点的接口,如add()、remove()、getChild()、print()等操作。由于菜单项不需要支持
add()或getChild(),而菜单可以包含其他菜单或菜单项,因此这些操作在基类中默认抛出UnsupportedOperationException,具体的子类可以根据需要覆盖这些方法。 -
Menu:组合对象类,代表一个菜单,可以包含多个MenuComponent(既可以是子菜单,也可以是菜单项)。它的主要功能是管理子菜单和菜单项,提供添加、删除、获取子组件的操作。 -
MenuItem:叶节点类,代表一个具体的菜单项。它没有子节点,所以不能包含其他MenuComponent,只能提供自己的基本信息,如名称、描述和价格。 -
Waitress:客户端类,接收一个MenuComponent,可以遍历和打印所有菜单和菜单项。它对组合和叶节点一视同仁,只调用print()方法即可打印出菜单结构。
3. 组合模式的工作原理
通过 MenuComponent 抽象类,Waitress 可以直接使用 MenuComponent 的统一接口,来处理菜单和菜单项的组合结构。组合模式的核心在于它让我们能够像处理单个对象一样,处理整个对象的组合。无论是遍历单个菜单项还是遍历包含多个子菜单的菜单,Waitress 类只需要关心调用通用的 print() 方法。
4. 组合模式的优点
-
一致性:客户端可以一致地处理叶节点(菜单项)和组合节点(菜单),使得客户端代码变得简洁且灵活。
在我们实现的
Waitress类中,它只关心如何遍历和打印MenuComponent,不需要区分是在处理一个具体的菜单项还是一个包含子菜单的组合对象。 -
可扩展性:添加新的菜单或菜单项变得非常简单。我们可以在组合中嵌套更多的子菜单,也可以轻松添加新的菜单项,甚至在
Menu中组合多个Menu。 -
简化客户端代码:由于客户端只需要处理抽象基类
MenuComponent,不需要分别处理MenuItem和Menu,客户端代码变得非常简洁。这种设计也让系统变得更灵活和可维护。
5. 组合模式中的递归
组合模式的核心在于它是一个递归结构:菜单可以包含子菜单,子菜单又可以包含更多的菜单或菜单项。这种递归结构允许我们通过遍历树形结构来处理所有的菜单项。Menu 的 print() 方法就是递归调用,遍历所有子菜单和菜单项。
6. 与迭代器模式的对比
虽然组合模式和迭代器模式都能处理多个对象,但它们的目标和使用场景有所不同:
-
迭代器模式:用于顺序遍历一个集合中的元素,通常用在结构较为扁平的情况下,例如遍历数组或列表中的元素。之前的实现中,我们通过多个
Iterator来遍历不同菜单,这更适合扁平化的数据结构。 -
组合模式:适合处理树形结构(如菜单-子菜单-菜单项),在这种情况下,组合模式能够让客户端以统一的方式处理复杂的层次结构。
相关文章:
组合模式(Composite Pattern)
使用组合模式(Composite Pattern)是一个更优雅的方式来表示菜单和菜单项。组合模式允许我们将单个对象(如菜单项)和组合对象(如菜单)以相同的方式处理。 解决方案: 创建组合结构:我…...
教你制作一本加密的样本册
在这个信息的时代,保护自己的隐私和知识产权变得尤为重要。你有没有想过,如何将自己珍贵的样本资料变成一本只有自己才能查看的加密宝典?今天,我就来教你制作一本加密的样本册 第一步,打开浏览器,搜索FLBOO…...
C语言进阶【1】--字符函数和字符串函数【1】
本章概述 字符分类函数字符转换函数strlen的使用和模拟实现strcpy的使用和模拟实现strcat的使用和模拟实现strcmp的使用和模拟实现彩蛋时刻!!! 字符分类函数 字符: 这个概念,我们在以前的文章中讲过了。我们键盘输入的…...
git提交自动带上 Signed-off-by信息
为了确保在使用 Signed-off-by 签名的同时保留你的提交消息,你需要修改 prepare-commit-msg 钩子脚本,以便它不会丢失原始的提交信息。 增加prepare-commit-msg 钩子以保留提交消息 prepare-commit-msg 钩子的目的是在提交信息文件中插入额外的内容&am…...
图论(2)
一、度 度统计的是一个节点上又多少条边 度出度入度 出度:统计以该节点为起始点箭头指向外面的边的条数 入度:统计箭头指向该节点的边数 度为1的节点为悬挂节点,边为悬挂边 用矩阵计算节点的度 二、握手定理 比如这里第一个集合里面有三…...
ASP.NET Core 入门教学十九 依赖注入ioc
ASP.NET Core内置了对依赖注入(Dependency Injection,简称DI)的支持,这是一种设计模式,用于实现控制反转(Inversion of Control,简称IoC),从而使得应用程序组件之间的耦合…...
omm kill 内存碎片化
内存频繁 OOM(Out of Memory)会导致内存碎片化,并进一步加剧无可用内存分配的问题。碎片化是内存管理中常见的问题,当系统频繁分配和释放内存时,内存空间会被分割成许多小块,虽然内存总量可能足够,但这些小块无法满足较大进程或数据的内存需求,最终导致系统无法找到足够…...
JS中给元素添加事件监听器的各种方法详解(包含比较和应用场景)
JavaScript 中给元素添加事件监听器的各种方法详解 在 JavaScript 中,事件处理是前端开发的一个重要部分。无论是点击按钮、提交表单,还是鼠标悬停,都涉及到事件监听。本文中,我将详细讲解各种给元素添加事件监听器的方法&#x…...
Python基本数据类型之复数complex
来源: “码农不会写诗”公众号 链接:Python基本数据类型之复数complex 文章目录 01 基本概念02 基本运算03 拓展1复数与向量 复数complex Python基本数据之复数(complex)即包含实部和虚部的数字。 01 基本概念 即包含实部和虚部的数字。 在Python中&am…...
第六届机器人与智能制造技术国际会议 (ISRIMT 2024)
目录 会议详情 主题 会议官网 会议详情 第六届机器人与智能制造技术国际研讨会(ISRIMT 2024)计划于2024年9月20-22日在常州举行。会议主要聚焦“机器人”和“智能制造技术”的研究领域,旨在为机器人和智能制造技术领域的专家学者、工程技术…...
鸿蒙轻内核M核源码分析系列十九 Musl LibC
往期知识点记录: 鸿蒙(HarmonyOS)应用层开发(北向)知识点汇总 轻内核M核源码分析系列一 数据结构-双向循环链表 轻内核M核源码分析系列二 数据结构-任务就绪队列 鸿蒙轻内核M核源码分析系列三 数据结构-任务排序链表 轻…...
mysqldump备份恢复数据库
mysqldump程序可以用来备份和恢复数据库 ,默认情况mysqldump会创建drop table, create table,和insert into的sql语句. 语法 > mysqldump [options] db_name [tbl_name ...] > mysqldump [options] --databases db_name ... > mysqldump [options] --all-databases备…...
路径规划——RRT算法
路径规划——RRT算法 算法原理 RRT算法的全称是快速扩展随机树算法(Rapidly Exploring Random Tree),它的思想是选取一个初始点作为根节点,通过随机采样,增加叶子节点的方式,生成一个随机扩展树,当随机树中的叶子节点…...
OPCUA-PLC
下载opcua服务器(有PLC可以直连),UaAnsiCServer下载路径 双击运行如下,Endpoint显示opcua服务路径 opc.tcp://DESKTOP-9SD7K4B:48020 下载opcua客户端(类似编写代码连接操作),UaExpert下载路径 如果连接失败,有一个授权认证,点击同意就行 java代码实现连接opcUA操作 pom.…...
在Windows系统上部署PPTist并实现远程访问
在Windows系统上部署PPTist并实现远程访问 前言PPTist简介本地部署PPTist步骤1:获取PPTist步骤2:安装依赖步骤3:运行PPTist 使用PPTist远程访问PPTist步骤1:安装Cpolar步骤2:配置公网地址步骤3:配置固定公网…...
【Grafana】Prometheus结合Grafana打造智能监控可视化平台
✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,…...
隐私计算实训营:SplitRec:当拆分学习遇上推荐系统
拆分学习的概念 拆分学习的核心思想是拆分网络结构。每一个参与方拥有模型结构的一部分,所有参与方的模型合在一起形成一个完整的模型。训练过程中,不同参与方只对本地模型进行正向或者反向传播计算,并将计算结果传递给下一个参与方。多个参…...
存在nginx版本信息泄露(请求头中存在nginx中间件版本信息)
在Nginx的配置文件中,server_tokens指令用于控制Nginx在HTTP响应头中包含的服务器版本信息,默认为true,开启状态。当设置为off时,Nginx将不会在响应头中包含任何服务器版本信息,仅显示“Server: nginx”这一行…...
在js中观察者模式讲解
在JavaScript中,观察者模式(Observer Pattern)是一种设计模式,允许一个对象(被观察者,Subject)维护一个依赖它的对象列表(观察者,Observer),并在它自身状态发生变化时自动通知这些观察者。观察者模式的典型使用场景包括事件系统、数据绑定和实时更新等情况。 一 、…...
java常用面试题-基础知识分享
什么是Java? Java是一种高级编程语言,旨在提供跨平台的解决方案。它是一种面向对象的语言,具有简单、结构化、可移植、可靠、安全等特点。 Java的主要特点是什么? Java的主要特点包括: 简单性:Java的语法…...
基于变分自编码器的类星体光谱无监督分析:QUEST工具原理与实践
1. 项目概述与核心价值如果你也和我一样,长期和斯隆数字巡天(SDSS)这类大型巡天项目产生的海量光谱数据打交道,那你一定理解那种感受:面对动辄数十万条光谱,传统的基于模板匹配或人工特征提取的分析方法&am…...
C++ 智能指针简介
文章目录1.由来2.基本思想3.引用计数4.实现模板参考文献1.由来 C 动态内存管理是通过一对运算符来完成的,new 用于申请内存空间,调用对象构造函数初始化对象并返回指向该对象的指针。delete 接收一个动态对象的指针,调用对象的析构函数销毁对…...
3步快速上手:终极AI图像增强工具Real-ESRGAN完全指南
3步快速上手:终极AI图像增强工具Real-ESRGAN完全指南 【免费下载链接】Real-ESRGAN Real-ESRGAN aims at developing Practical Algorithms for General Image/Video Restoration. 项目地址: https://gitcode.com/gh_mirrors/re/Real-ESRGAN 你是否曾经为模糊…...
一小时搭建爬虫数据提取智能体 · 数据矿工
🧑💻 博主介绍 & 诚邀关注 作者:专注于 Java、Python、前端开发的技术博主 | 全网粉丝 30 万 在校期间协助导师完成毕业设计课题分类、论文格式初审及代码整理工作;工作后持续分享毕设思路,助力毕业生顺利完成…...
2026最好用的图片处理工具推荐:去水印 / 抠图 / 高清化实测对比
2026最好用的图片处理工具推荐:去水印 / 抠图 / 高清化实测对比 前言:一张图片毁掉一个项目?别让烂工具耽误你 2026年,AI图片处理技术早已不是三年前的水平。发丝级抠图、去水印无痕、超分辨率重建……这些功能听起来很美好&…...
从背包UI到聊天框:详解Unity ScrollRect在不同游戏场景下的实战应用与优化
从背包UI到聊天框:Unity ScrollRect全场景实战指南在RPG游戏的背包界面滑动查看装备,在社交系统中翻阅聊天记录,或是横向浏览角色画廊——这些看似不同的交互背后,都依赖同一个核心组件:Unity的ScrollRect。作为UGUI体…...
别再为单细胞数据批次效应发愁了!手把手教你用Harmony算法搞定整合分析
单细胞数据整合实战:用Harmony消除批次效应的完整指南当你在不同时间、不同实验室或使用不同平台获得多个单细胞RNA测序数据集时,最令人头疼的问题莫过于批次效应——这种技术性差异会掩盖真实的生物学信号。想象一下,你精心设计的实验因为数…...
深度学习篇---NVIDIA DeepStream
NVIDIA DeepStream 是一个功能强大的流媒体分析工具包,专为基于 AI 的多传感器处理、视频、音频和图像理解而设计。你可以把它想象成一个“视觉 AI 应用的乐高工厂”,它把视频解码、AI 推理、目标追踪这些复杂的“零件”,巧妙地组合成一条高效…...
GEO优化是不是免费引流方式
这个问题很多企业主会问,背后关心的是获客成本。要回答清楚,需要区分几个概念:流量获取本身是否付费、优化过程是否产生成本、长期来看性价比如何。GEO本身属于“自然流量”获取方式从本质上讲,GEO优化获取的流量是自然流量&#…...
DeepSeek总结的clickhousectl v0.2.0: Postgres, ClickPipes 等更多功能
来源:https://clickhouse.com/blog/clickhousectl-v0-2-0-postgres-clickpipes-more clickhousectl v0.2.0: Postgres, ClickPipes 等更多功能 作者: Al Brown 日期: 2026年5月20日 阅读时间: 7分钟 我们发布了 clickhousectl v0.2.0。此版本增加了对 Postgres&…...
