津津乐道设计模式 - 组合模式详解(以餐厅菜单系统举例让你快速掌握)

news2024/10/7 16:17:53

在这里插入图片描述

😄 19年之后由于某些原因断更了三年,23年重新扬帆起航,推出更多优质博文,希望大家多多支持~
🌷 古之立大事者,不惟有超世之才,亦必有坚忍不拔之志
🎐 个人CSND主页——Micro麦可乐的博客
🐥《Docker实操教程》专栏以最新的Centos版本为基础进行Docker实操教程,入门到实战
🌺《RabbitMQ》本专栏主要介绍使用JAVA开发RabbitMQ的系列教程,从基础知识到项目实战
🌸《设计模式》专栏以实际的生活场景为案例进行讲解,让大家对设计模式有一个更清晰的理解
如果文章能够给大家带来一定的帮助!欢迎关注、评论互动~

津津乐道设计模式 - 组合模式详解

  • 什么是组合模式
  • 组合模式适用场景
  • 生活场景
  • 代码案例
  • 组合模式的优缺点
  • 结语

什么是组合模式

组合模式是一种结构型设计模式,它允许将对象组合成树形结构以表示“整体-部分”的层次关系。组合模式使得用户对单个对象和组合对象的使用具有一致性,可以用统一的方式处理它们

组合模式由以下几个角色组成:

组件(Component):定义了组合对象和叶子对象的共有方法和属性,并且可以为子对象提供默认的实现。它可以是抽象类或接口。

叶子(Leaf):表示组合中的叶子对象,没有子对象。

组合(Composite):表示组合中的组合对象,可以包含子对象。它实现了组件的方法,并在需要的情况下调用子对象的方法。

客户端(Client):通过组合模式的接口来操作组合对象和叶子对象。

在这里插入图片描述

组合模式适用场景

  • 当需要表示对象的部分-整体层次结构,并希望以统一的方式处理整体和部分时,可以使用组合模式。例如,文件系统中的目录和文件的层次结构。
  • 当希望客户端忽略组合对象和叶子对象的区别,统一地对待它们时,可以使用组合模式。客户端不需要知道当前操作的是组合对象还是叶子对象,只需要调用统一的接口即可。
  • 当希望对组合对象和叶子对象进行增加、删除、遍历等操作时,可以使用组合模式。组合模式使得对整个树形结构的操作更加简单和灵活。

需要注意的是,组合模式并不是为了解决对象的层次关系而存在的,而是通过将对象组合成树形结构来统一处理整体和部分的关系。在应用组合模式时,需要合理划分组合对象和叶子对象,并确保它们之间的接口和行为一致性,以达到更好的扩展性和灵活性。

生活场景

在餐厅中,菜单通常是由多个菜品组成的。我们可以将菜单系统抽象为一个树形结构,其中树的节点表示菜单或菜品分类,而叶子节点表示具体的菜品。

在这里插入图片描述

餐厅的菜单系统中可能包含以下节点和叶子节点:

菜单节点(Menu):表示一个完整的菜单,可能包含多个菜品或菜单分类

菜单分类节点(Menu Category):表示菜单中的分类,如主菜、甜点、饮品等。这个节点可以包含多个叶子节点和子菜单节点

菜品节点(Dish):表示具体的菜品,包含菜品的名称、描述和价格等信息

通过组合模式,我们可以使用统一的接口来操作菜单系统。无论是获取整个菜单,还是获取某个分类下的菜品,我们都可以使用相同的方式来处理整体和部分。

例如,我们可以调用菜单节点的方法来获取整个菜单的内容,并在其中递归调用子菜单节点的方法来获取菜单分类下的具体菜品。同样地,我们也可以调用菜单分类节点的方法来获取该分类下的菜品列表。

组合模式的好处在于,我们可以方便地对菜单系统进行扩展和修改,无需改变客户端的代码。例如,如果菜单中新增了一个新的菜品分类,我们只需要添加一个新的菜单分类节点即可,而不会影响到其他部分。

总而言之,组合模式可以让我们以统一的方式处理菜单系统中的整体和部分,提供了更好的扩展性和灵活性。

代码案例

首先,我们定义一个抽象的组件接口 MenuComponent,表示菜单系统中的组件(菜单、菜单分类和菜品):

public interface MenuComponent {
    void display();
}

然后,我们创建具体的菜单节点类 Menu,表示一个完整的菜单:

import java.util.ArrayList;
import java.util.List;

public class Menu implements MenuComponent {
    private String name;
    private List<MenuComponent> components;

    public Menu(String name) {
        this.name = name;
        this.components = new ArrayList<>();
    }

    public void addComponent(MenuComponent component) {
        components.add(component);
    }

    public void removeComponent(MenuComponent component) {
        components.remove(component);
    }

    @Override
    public void display() {
        System.out.println("菜单:" + name);
        System.out.println("--------------------");
        for (MenuComponent component : components) {
            component.display();
            System.out.println();
        }
    }
}

接下来,我们创建菜单分类节点类 MenuCategory,表示菜单中的分类:

import java.util.ArrayList;
import java.util.List;

public class MenuCategory implements MenuComponent {
    private String name;
    private List<MenuComponent> components;

    public MenuCategory(String name) {
        this.name = name;
        this.components = new ArrayList<>();
    }

    public void addComponent(MenuComponent component) {
        components.add(component);
    }

    public void removeComponent(MenuComponent component) {
        components.remove(component);
    }

    @Override
    public void display() {
        System.out.println("菜单分类:" + name);
        System.out.println("*******************");
        for (MenuComponent component : components) {
            component.display();
        }
    }
}

最后,我们创建菜品节点类 Dish,表示具体的菜品:

public class Dish implements MenuComponent {
    private String name;
    private String description;
    private double price;

    public Dish(String name, String description, double price) {
        this.name = name;
        this.description = description;
        this.price = price;
    }

    @Override
    public void display() {
        System.out.println("菜品:" + name);
        System.out.println("描述:" + description);
        System.out.println("价格:" + price + "元");
    }
}

现在,我们可以在客户端代码中使用组合模式来操作菜单系统:

public class CombinationPatternTest {
    public static void main(String[] args) {
        // 创建菜单
        Menu menu = new Menu("餐厅菜单");

        // 创建菜单分类
        MenuCategory mainCourse = new MenuCategory("主菜");
        MenuCategory dessert = new MenuCategory("甜点");

        // 创建具体菜品
        Dish steak = new Dish("牛排", "美味的牛排", 50.0);
        Dish pasta = new Dish("意大利面", "经典的意式面食", 30.0);
        Dish cake = new Dish("蛋糕", "甜甜的蛋糕", 20.0);

        // 将菜品添加到相应的菜单分类中
        mainCourse.addComponent(steak);
        mainCourse.addComponent(pasta);
        dessert.addComponent(cake);

        // 将菜单分类添加到菜单中
        menu.addComponent(mainCourse);
        menu.addComponent(dessert);

        // 显示整个菜单
        menu.display();
    }
}

通过以上代码,我们创建了一个餐厅的菜单系统,并使用组合模式来组织菜单、菜单分类和菜品之间的关系。客户端代码可以通过调用菜单的 display() 方法来展示整个菜单的内容。

组合模式的优缺点

组合模式的优点:

  • 简化客户端代码:组合模式通过统一的接口,使得客户端可以一致地处理单个对象和组合对象,无需区分它们的具体类型,从而简化了客户端代码的复杂性。
  • 增加新组件方便:由于组合模式使用了递归结构,因此添加新的组件非常方便。无论是添加一个新的叶子节点还是一个新的组合节点,都可以通过修改少量代码来完成。
  • 灵活性和扩展性:组合模式使得系统具备了较高的灵活性和可扩展性。可以根据需要构建复杂的组合结构,随时增加、删除或替换组件,而无需修改现有代码。

组合模式的缺点:

  • 设计复杂性增加:由于组合模式涉及到递归和层次结构的组织,因此设计和实现时会增加一定的复杂性,特别是在处理具有多层嵌套的组合结构时。
  • 不适合所有情况:组合模式更适合表示树形结构的场景,如果对象之间的关系不是层次化的,或者对象的行为不一致,使用组合模式可能不太合适。
  • 运行效率低:在使用组合模式时,由于需要通过递归遍历整个组合结构,可能会导致性能下降。特别是当组合结构很大时,遍历的时间会显著增加。

综上所述,组合模式通过统一接口、灵活的组合结构和简化客户端代码的特点,为处理树形结构的对象提供了一种有效的方式。然而,使用组合模式需要权衡设计复杂性和运行效率,确保在合适的场景下使用。

结语

本章节主要介绍了组合模式、组合模式适用场景、组合模式的优缺点,并以餐厅菜单的生活场景模拟组合模式的样例代码,如果本文对你有用,欢迎关注收藏评论,后续将陆续推出贴切生活的搞笑讲解方式带大家一起学编程~

样例代码:https://github.com/lhmyy521125/toher-designmode

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/692175.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于Python所写的玛丽冒险设计

点击以下链接获取源码资源&#xff1a; https://download.csdn.net/download/qq_64505944/87953199 《玛丽冒险》程序使用说明 在PyCharm中运行《玛丽冒险》即可进入如图1所示的游戏主界面。 图1 游戏主界面 具体的操作步骤如下&#xff1a; &#xff08;1&#xff09;游戏…

数据库监控与调优【十四】—— COUNT语句优化

COUNT语句优化 有关COUNT的几个实验与结论 准备工作 create table user_test_count (id int primary key not null auto_increment,name varchar(45),age int,email varchar(60),birthday date ) engine innodb;insert into user_test_count (id, name, a…

算法设计与分析之回溯法

文章目录 1. 回溯法简介1.1 DFS的基本思想1.2 回溯法的基本思想1.3 回溯法和DFS的区别1.4 剪枝 2. 01背包问题&#xff1a;子集树2.1 问题介绍2.2 解决思路2.3 算法实现2.4 如何优化 3. 旅行商问题TSP&#xff1a;排序树3.1 问题介绍3.2 解决思路3.3 算法框架3.4 算法实现 4. 总…

项目一点点记录

kafka发布通知 kafka是消息队列&#xff0c;kafka采用发布订阅模式进行消息的生产与消费。在项目中&#xff0c;我们采用spring来整合kafka&#xff0c; 通过定义事件event来封装 点赞、关注、评论三类事件&#xff0c;event实体中有 事件主题topic&#xff0c;当前用户id&…

怎么给PDF添加图片水印?其实很简单,看这篇就会了!

许多人都意识到版权问题的重要性&#xff0c;尽管在日常生活中我们可能很少遇到&#xff0c;但在办公和学习中却经常涉及到此类问题。例如&#xff0c;我们辛辛苦苦制作的PDF文件&#xff0c;如何确保不被他人盗用呢?这就涉及到如何为PDF添加图片水印的问题&#xff0c;相当于…

无向图G的广度优先搜索和深度优先搜索以及完整程序

图的遍历算法有两种&#xff1a;广度优先搜索和深度优先搜索 一.广度优先搜索类似于层次遍历&#xff0c;需要借助辅助队列 空间复杂度为O(|V|);空间复杂度由辅助队列大小决定 时间复杂度为O(|V||E|) 为避免同一顶点被多次访问&#xff0c;设计visited[]来标记顶点 二.深度…

MyBatis 从初识到掌握

目录 今日良言&#xff1a;与其抱怨于黑暗&#xff0c;不如提灯向前行 一、初识MyBatis 1.MyBatis定义 2.为什么要学习MyBatis 3.MyBatis的创建 二、MyBatis的相关操作 1.增删改查操作 2.动态SQL使用 今日良言&#xff1a;与其抱怨于黑暗&#xff0c;不如提灯向前行 一…

UE4/5 通过Control rig的FullBody【蜘蛛模型,不用basic ik】

目录 根设置 FullBody IK 额外骨设置 ​编辑 晃动效果 根设置 第一步你需要准备一个蜘蛛模型&#xff0c;不论是官方示例或者是epic上购买的模型 然后我用的是epic上面购买的一个眼球蜘蛛&#xff1a; 第一步&#xff0c;我们从根创建一个空项【这个记得脱离父子级到root之…

SQLServer 2016 R2数据库新建、附加、分离、备份、还原、复制等基本操作

一、打开Microsoft SQL Server Management Studio 在桌面上找到图标&#xff0c;双击运行 打开Microsoft SQL Server Management Studio 17 输入服务器名称&#xff0c;选择SQL Server 身份验证&#xff0c;sa和sa密码&#xff0c;可以勾选记住密码&#xff0c;以便以后的登录…

分享基于安卓项目的单元测试总结

前言&#xff1a; 负责公司的单元测试体系的搭建&#xff0c;大约有一两个月的时间了&#xff0c;从最初的框架的调研&#xff0c;到中期全员的培训&#xff0c;以及后期对几十个项目单元测试的引入和推进&#xff0c;也算是对安卓的单元测试有了一些初步的收获以及一些新的认…

【雕爷学编程】Arduino动手做(131)---跑马灯矩阵键盘模块

37款传感器与执行器的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止这37种的。鉴于本人手头积累了一些传感器和执行器模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&am…

线性代数基础--矩阵

矩阵 矩阵是由排列在矩形阵列中的数字或其他数学对象组成的表格结构。它由行和列组成&#xff0c;并且在数学和应用领域中广泛使用。 基本概念 元素&#xff1a;矩阵中的每个数字称为元素。元素可以是实数、复数或其他数学对象。 维度&#xff1a;矩阵的维度表示矩阵的行数和…

vtk创建点

使用vtk库创建三维空间中的点 引言开发环境示例一项目结构实现代码 运行效果示例二项目结构实现代码 运行效果总结 引言 本文仅适合初学者。 本文不提供vtk动态库的生成&#xff0c;以及在QtCreator中的引进vtk时的配置。 本文先由示例一开始&#xff0c;然后再在示例一的基础…

aws使用外部 ID对其他账号授权

点击前往授权,进入控制台 https://signin.aws.amazon.com/signin?redirect_urihttps%3A%2F%2Fconsole.aws.amazon.com%2Fconsole%2Fhome%3FhashArgs%3D%2523%26isauthcode%3Dtrue%26state%3DhashArgsFromTB_eu-north-1_f2d9c316b93c0026&client_idarn%3Aaws%3Asignin%3A%…

Glassdoor美国公司员工及面试者评价数据

一、数据简介 除了股东、债权人、政府等外部利益相关者外&#xff0c;员工的利益更应该得到公司的恰当保护&#xff0c;因为员工才是公司创造价值的真正主体。提高企业在产品市场的竞争力&#xff0c;首先就是要提高员工对企业的满意度&#xff0c;只有员工的满意度更高、幸福感…

7个技巧,助你同时轻松管理和跟踪多个项目

仅仅想到要兼顾这么多重要的职责&#xff0c;就会让许多专业的项目经理感到焦虑。当涉及多个项目的多种项目管理工具的处理&#xff0c;即使对于了解项目管理的项目经理来说&#xff0c;也是一项艰巨的任务&#xff0c;而对于在这个领域没有经过适当培训的人来说&#xff0c;这…

强化学习从基础到进阶--案例与实践[7.1]:深度确定性策略梯度DDPG算法、双延迟深度确定性策略梯度TD3算法详解项目实战

【强化学习原理项目专栏】必看系列&#xff1a;单智能体、多智能体算法原理项目实战、相关技巧&#xff08;调参、画图等、趣味项目实现、学术应用项目实现 专栏详细介绍&#xff1a;【强化学习原理项目专栏】必看系列&#xff1a;单智能体、多智能体算法原理项目实战、相关技巧…

计算机网络—数据链路层

文章目录 数据链路层服务差错编码多路访问协议信道划分随机访问MAC协议 数据链路层服务 该层中的帧数据结构&#xff1a; 帧头部会因为不同的局域网协议而不同&#xff0c;因此会在另一篇博文中继续介绍不同的帧数据报&#xff0c;不在本博文介绍。&#xff08;不过除了PPP协…

Docker学习笔记11

Docker容器镜像&#xff1a; 1&#xff09;docker client 向docker daemon发起创建容器的请求&#xff1b; 2&#xff09;docker daemon查找本地有客户端需要的镜像&#xff1b; 3&#xff09;如无&#xff0c;docker daemon则到容器的镜像仓库中下载客户端需要的镜像&#…

线性代数基础--向量

目录 向量的概念 基本概念 抽象概念 向量的意义 几何意义 物理意义 欧式空间 特点和性质 行向量与列向量 行向量 列向量 两者的关系 向量的基本运算与范数 向量的基本运算 向量的加法 数乘运算&#xff08;实数与向量相乘&#xff09; 转置 向量的范数 向量…