Java 设计模式——组合模式

目录

  • 1.概述
  • 2.结构
  • 3.实现
    • 3.1.抽象构件
    • 3.2.容器构件
    • 3.3.叶子节点
    • 3.4.测试
  • 4.分类
  • 5.使用场景
  • 6.优点

1.概述

在这里插入图片描述

(1)大家对于上面这个图片肯定非常熟悉,上图我们可以看做是一个文件系统,对于这样的结构我们称之为树形结构。在树形结构中可以通过调用某个方法来遍历整个树,当我们找到某个叶子节点后,就可以对叶子节点进行相关的操作。可以将这颗树理解成一个大的容器,容器里面包含很多的成员对象,这些成员对象即可是容器对象也可以是叶子对象。但是由于容器对象和叶子对象在功能上面的区别,使得我们在使用的过程中必须要区分容器对象和叶子对象,但是这样就会给客户带来不必要的麻烦,作为客户而已,它始终希望能够一致的对待容器对象和叶子对象。

(2)组合模式 (Composite Pattern),又名部分整体模式,是一种结构型模式,它用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。这种类型的设计模式创建了对象组的树形结构。

2.结构

组合模式主要包含以下三种角色:

  • 抽象构件 (Component):定义组合中对象的共有方法和属性,并可以提供默认的实现。它可以是抽象类或接口。
  • 容器构件 (Composite):表示组合中的容器节点对象,可以包含其他的子节点。实现了抽象构件的接口,并在容器中存储子节点。
  • 叶子构件 (Leaf):表示组合中的叶子节点对象,它没有子节点。实现了抽象构件的接口。

3.实现

【例】软件菜单:如下图,我们在访问别的一些管理系统时,经常可以看到类似的菜单。一个菜单可以包含菜单项(菜单项是指不再包含其他内容的菜单条目),也可以包含带有其他菜单项的菜单,因此使用组合模式描述菜单就很恰当,我们的需求是针对一个菜单,打印出其包含的所有菜单以及菜单项的名称。

在这里插入图片描述

类图如下:
在这里插入图片描述
具体代码如下:

3.1.抽象构件

MenuComponent.java

/*
	不管是菜单还是菜单项,都应该继承自统一的接口,这里暂时将这个统一的接口称为菜单组件。
	菜单组件,属于抽象根节点
*/
public abstract class MenuComponent {
    //菜单组件的名称
    protected String name;
    //菜单组件的层级
    protected int level;
    
    //添加子菜单
    public void add(MenuComponent menuComponent){
        throw new UnsupportedOperationException();
    }
    
    //移除子菜单
    public void remove(MenuComponent menuComponent){
        throw new UnsupportedOperationException();
    }
    
    //获取指定的子菜单
    public MenuComponent getChild(int index){
        throw new UnsupportedOperationException();
    }
    
    //获取菜单或者菜单项的名称
    public String getName(){
        return name;
    }
    
    //打印菜名称的方法(包含子菜单和子菜单项)
    public abstract void print();
}

这里的MenuComponent定义为抽象类,因为有一些共有的属性和行为要在该类中实现,Menu和MenuItem类就可以只覆盖自己感兴趣的方法,而不用搭理不需要或者不感兴趣的方法,举例来说,Menu类可以包含子菜单,因此需要覆盖add()、remove()、getChild()方法,但是MenuItem就不应该有这些方法。这里给出的默认实现是抛出异常,也可以根据自己的需要改写默认实现。

3.2.容器构件

Menu.java

//菜单类,属于树枝节点
public class Menu extends MenuComponent{
    
    //菜单可以有多个子菜单或者子菜单项
    private List<MenuComponent> menuComponentList = new ArrayList<MenuComponent>();
    
    //构造方法
    public Menu(String name,int level){
        this.name = name;
        this.level = level;
    }
    
    @Override
    public void add(MenuComponent menuComponent) {
        menuComponentList.add(menuComponent);
    }
    
    @Override
    public void remove(MenuComponent menuComponent) {
        menuComponentList.remove(menuComponent);
    }
    
    @Override
    public MenuComponent getChild(int index) {
        return menuComponentList.get(index);
    }
    
    @Override
    public void print() {
        //打印菜单名称
        for (int i = 0; i < level; i++) {
            System.out.print("--");
        }
        System.out.println(name);
        
        //打印子菜单或者子菜单项名称
        for (MenuComponent component : menuComponentList) {
            component.print();
        }
    }
}

Menu类已经实现了除了getName方法的其他所有方法,因为Menu类具有添加菜单,移除菜单和获取子菜单的功能。

3.3.叶子节点

MenuItem.java

//菜单项类,属于叶子节点
public class MenuItem extends MenuComponent{
    
    public MenuItem(String name,int level){
        this.name = name;
        this.level = level;
    }
    
    @Override
    public void print() {
        for (int i = 0; i < level; i++) {
            System.out.print("--");
        }
        //打印菜单项的名称
        System.out.println(name);
    }
}

MenuItem是菜单项,不能再有子菜单,所以添加菜单,移除菜单和获取子菜单的功能并不能实现。

3.4.测试

Client.java

public class Client {
    public static void main(String[] args) {
        //创建菜单树
        MenuComponent menu1 = new Menu("菜单管理", 2);
        menu1.add(new MenuItem("页面访问", 3));
        menu1.add(new MenuItem("展开访问", 3));
        menu1.add(new MenuItem("编辑访问", 3));
        menu1.add(new MenuItem("删除访问", 3));
        menu1.add(new MenuItem("新增访问", 3));
        
        MenuComponent menu2 = new Menu("权限管理", 2);
        menu2.add(new MenuItem("页面访问", 3));
        menu2.add(new MenuItem("提交保存", 3));
        
        MenuComponent menu3 = new Menu("角色管理", 2);
        menu3.add(new MenuItem("页面访问", 3));
        menu3.add(new MenuItem("新增角色", 3));
        menu3.add(new MenuItem("修改保存", 3));
        
        //创建以及菜单
        MenuComponent component = new Menu("系统管理", 1);
        //将二级菜单添加到一级菜单中
        component.add(menu1);
        component.add(menu2);
        component.add(menu3);
        
        //打印菜单名称(如果有子菜单则一起打印)
        component.print();
    }
}

输出结果如下:

--系统管理
----菜单管理
------页面访问
------展开访问
------编辑访问
------删除访问
------新增访问
----权限管理
------页面访问
------提交保存
----角色管理
------页面访问
------新增角色
------修改保存

4.分类

在使用组合模式时,根据抽象构件类的定义形式,我们可将组合模式分为透明组合模式安全组合模式两种形式:

  • 透明组合模式
    • 透明组合模式中,抽象根节点角色中声明了所有用于管理成员对象的方法,比如在示例中 MenuComponent 声明了 add()remove()getChild() 方法,这样做的好处是确保所有的构件类都有相同的接口。透明组合模式也是组合模式的标准形式。
    • 透明组合模式的缺点是不够安全,因为叶子对象和容器对象在本质上是有区别的,叶子对象不可能有下一个层次的对象,即不可能包含成员对象,因此为其提供 add()、remove() 等方法是没有意义的,这在编译阶段不会出错,但在运行阶段如果调用这些方法可能会出错(如果没有提供相应的错误处理代码)
  • 安全组合模式:在安全组合模式中,在抽象构件角色中没有声明任何用于管理成员对象的方法,而是在树枝节点 Menu 类中声明并实现这些方法。安全组合模式的缺点是不够透明,因为叶子构件和容器构件具有不同的方法,且容器构件中那些用于管理成员对象的方法没有在抽象构件类中定义,因此客户端不能完全针对抽象编程,必须有区别地对待叶子构件和容器构件。

5.使用场景

(1)组合模式适合以下场景:

  • 表示部分和整体的层次结构:例如,文件系统中的文件和目录之间的关系、菜单和菜单项之间的关系等,都可以使用组合模式来表示。
  • 递归遍历结构:当需要遍历复杂的树形结构,而不管它的具体层次,可以使用组合模式来递归处理整个结构,从而简化代码实现。
  • 具有共性操作的场景:当一个类或一组类具有一些共性操作,但是这些操作可能应用于单个对象或多个对象的集合时,可以使用组合模式来统一处理这些操作,使得代码更加简洁和易于扩展。

(2)总之,组合模式适用于需要使用树形结构表示对象的场景,能够提供一种优雅的设计和管理方式,提高代码灵活性和可扩展性。

6.优点

(1)组合模式的优点包括:

  • 简化客户端代码:组合模式使得客户端可以一致地对待单个对象和组合对象,无需区分它们的类型,从而简化了客户端的代码逻辑。
  • 灵活性和可扩展性:通过组合模式,可以方便地增加、删除和修改组合中的对象,使得整个树形结构具有很高的灵活性和可扩展性。
  • 递归处理:组合模式通过递归方式处理整个树形结构,使得操作变得简单,具有较好的递归性。

(2)组合模式的缺点包括:

  • 可能引入过多的细节:当树形结构非常复杂时,可能会引入更多的细节和复杂性,增加实现和理解的难度。
  • 不适合所有场景:组合模式更适用于表示"部分-整体"的层次结构,如文件夹包含文件等,对于无需表达层次关系的场景,使用组合模式可能会增加不必要的复杂性。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

全院级不良事件管理系统源码,事件上报、流转审批、数据统计、原因分析、措施制定

不良事件报告管理系统源码 事件上报、流转审批、数据统计、原因分析、措施制定 医院不良事件管理系统&#xff0c;支持医疗管理、护理管理、药品管理、医技管理、器械管理、输血管理、院感管理、职业防护管理、信息管理、后勤管理、治安管理等事件&#xff0c;内容齐全&#xf…

Java: 实现电影信息管理系统 (javaBean)

文章目录 电影信息系统1. 将JavaBean数据类封装好2.业务逻辑类3.测试类&#xff08;main函数类&#xff09; 电影信息系统 需求&#xff1a; 展示系统中的全部电影&#xff08;每部电影展示、名称、平分&#xff09;。允许用户根据电影编号&#xff08;id&#xff09;查询出某…

霸榜“双11”,高端文创新品发布即售罄,剑南春大促节点再传佳绩

执笔 | 洪大大 编辑 | 萧 萧 作为一年一度的重要促销大节&#xff0c;“双11”线上购物已成为各大品牌奋战的重要战场。而在今年&#xff0c;剑南春也是再一次拔得头筹。 在刚刚出炉的“双11”战报中&#xff0c;天猫剑南春官方旗舰店以超高人气和销售量获得白酒行业交易指…

大促期间治理品牌窜货的诀窍

渠道问题中&#xff0c;最常见的是窜货&#xff0c;窜货还会伴随低价&#xff0c;会影响其他经销商的利益&#xff0c;同时窜货还可能带来假货&#xff0c;所以治理窜货是品牌的责任&#xff0c;对于出货量巨大的双十一大促&#xff0c;品牌更应重视对窜货问题的治理。 力维网络…

开播4分钟就被限流的直播回顾!

虽然在双11正式开播之前做过几轮测试&#xff0c;但我们还是踩了坑&#xff01; 而离我们正式开播&#xff0c;才仅仅4分钟而已&#xff01; 双11直播回顾 在双11当天的19:15分&#xff0c;我们开始播放7分半的开播视频&#xff0c;完播两轮之后正好是计划的开播时间。 但…

ssm+vue的课堂管理系统(有报告)。Javaee项目,ssm vue前后端分离项目。

演示视频&#xff1a; ssmvue的课堂管理系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;ssm vue前后端分离项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&…

京东数据软件系统:京东销量和销额数据在哪里看?

京东平台店铺众多&#xff0c;行业同行也数不胜数&#xff0c;若想要在平台中更好的运营店铺&#xff0c;品牌需要做好数据分析。下面结合鲸参谋电商数据分析平台这一数据分析工具&#xff0c;我们来看一看品牌在做数据分析时需要注重哪些数据维度。 *行业数据 京东商家通过鲸…

windows qos api使用示例

简介 本文给出C版以及Rust版调用windows API实现QOS的示例&#xff0c;并提出一些注意事项。QOS可以用来区分socket 优先级、实现带宽控制等诸多功能。 UDP版本 该示例的重要部分是客户端应用程序。客户端代码的工作方式如下&#xff1a; 1. 通过套接字连接到目标 IP 地址。…

如何提取信号的包络

包络提取 如何提取信号的包络。 创建双边带振幅调制信号。载波频率为 1 kHz。调制频率为 50 Hz。调制深度为 100%。采样率为 10 kHz。 t 0:1e-4:0.1; x (1cos(2*pi*50*t)).*cos(2*pi*1000*t);plot(t,x) xlim([0 0.04]) 使用 hilbert 函数提取包络。包络是由 hilbert 计算的…

upload-labs关卡6(基于黑名单的大小写绕过)通关思路

文章目录 前言一、回顾靶场第五关二、靶场第六关需要了解的知识点1、window系统大小写不敏感2、linux系统大小写敏感 三、靶场第六关通关思路1.看源代码2.通过bp进行大小写绕过 总结 前言 此文章只用于学习和反思巩固文件上传漏洞知识&#xff0c;禁止用于做非法攻击。注意靶场…

大数据-之LibrA数据库系统告警处理(ALM-12041 关键文件权限异常)

告警解释 系统每隔一个小时检查一次系统中关键目录或者文件权限、用户、用户组是否正常&#xff0c;如果不正常&#xff0c;则上报故障告警。 当检查到权限等均正常&#xff0c;则告警恢复。 告警属性 告警ID 告警级别 可自动清除 12041 严重 是 告警参数 参数名称 …

【ATTCK】MITRE Caldera-插件

CALDERA是一个由python语言编写的红蓝对抗工具&#xff08;攻击模拟工具&#xff09;。它是MITRE公司发起的一个研究项目&#xff0c;该工具的攻击流程是建立在ATT&CK攻击行为模型和知识库之上的&#xff0c;能够较真实地APT攻击行为模式。 通过CALDERA工具&#xff0c;安全…

初始MySQL(二)(表的增删查改)

目录 修改表 CRUD(增删改查) insert语句(表中增加数据) update语句(修改表中的数据) delete删除语句 select语句 修改表 添加列 ALTER TABLE tablename ADD (column datatype [DEFAULT expr] [, column datatype] ...); 修改列 ALTER TABLE tablename MODIFY (column …

【杂记】WinServer 2019解决net3.5无法安装问题

1. 问题描述 由于QA环境中的OSCE V16服务端是部署在虚拟机Windows Server 2019上&#xff0c;当进行数据库的迁移操作时&#xff0c;操作系统会提示缺少.NET Framework 3.5组件(SQL Server数据库系统的运行需要依靠.NET Framework 3.5&#xff0c;但是Windows Server2019默认情…

VINS-Mono-后端优化 (四:边缘化原理)

滑窗中固定只能有11帧&#xff0c;当来了新的帧的时候旧的帧就需要抹掉&#xff0c;但是不能直接把旧的帧的全部信息抹掉&#xff0c;因为旧的帧的经历过11次优化&#xff0c;其地图点中的优化信息是有用&#xff0c;边缘化的操作就是为了只把最旧帧的变量去掉&#xff0c;但是…

iManager云套件支持配置kingbase

作者 yangjunlin 前言 越来越多的涉密单位对于信创环境的要求逐渐升高&#xff0c;服务应用对国产数据库的依赖性也在提高&#xff0c;针对超图iManager for k8s产品中的开源数据库替换为kingbase等国产化数据库的客户需求和场景也就随之而来&#xff0c;因此本文将带着读者一步…

【springboot】Failed to start bean ‘webServerStartStop‘;

新同事新建了一个项目springboot项目&#xff0c;启动时候报错。 具体错误如下&#xff1a; Failed to start bean webServerStartStop; nested exception is org.springframework.boot.web.server.WebServerException: Unable to start embedded Tomcat server 未能启动bea…

离散时间系统模型

离散时间系统模型 离散时间系统模型是表示数字滤波器的方案。MATLAB 科学计算环境支持若干种离散时间系统模型&#xff0c;这些模型将在以下章节中介绍&#xff1a; ​传递函数零极点增益状态空间部分分式展开式&#xff08;残差形式&#xff09;二阶节 (SOS)格型结构体卷积矩…

SpringBoot配置文件 —— 超详细全方位教程

&#x1f9f8;欢迎来到dream_ready的博客&#xff0c;&#x1f4dc;相信您也对这篇博客也感兴趣o (ˉ▽ˉ&#xff1b;) &#x1f9f8;祝诸君前程似锦&#xff0c;归来仍初心不忘&#xff01; &#x1f4dc;Spring&#xff0c;SpringBoot和SpringMVC的关系以及区别 —— 超准确…

Windows电脑训练 RT-DETR 改进算法 (Ultralytics) 教程,改进RTDETR算法(包括使用训练、验证、推理教程)

手把手从零开始训练 RT-DETR 改进项目 (Ultralytics版本) 教程,改进RTDETR算法 本文以Windows服务器为例:从零开始使用Windows训练 RT-DETR 算法项目 《芒果剑指 RT-DETR 目标检测算法 改进》 适用于芒果专栏改进RT-DETR算法 文章目录 百度 RT-DETR 算法介绍改进网络代码汇…
最新文章