结构型设计模式-组合模式

一、组合模式

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

定义

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

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

  1. 抽象根节点(Component):定义系统各层次对象的共有方法和属性,可以预先定义一些默认行为和属性。
  2. 树枝节点(Composite):定义树枝节点的行为,存储子节点,组合树枝节点和叶子节点形成一个树形结构。
  3. 叶子节点(Leaf):叶子节点对象,其下再无分支,是系统层次遍历的最小单位。

【例】软件菜单

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

要实现该案例,我们先画出类图:

代码实现:

不管是菜单还是菜单项,都应该继承自统一的接口,这里姑且将这个统一的接口称为菜单组件。

//菜单组件  不管是菜单还是菜单项,都应该继承该类
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 i){throw new UnsupportedOperationException();}//获取菜单名称public String getName(){return name;}public void print(){throw new UnsupportedOperationException();}
}

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

public class Menu extends MenuComponent {private List<MenuComponent> menuComponentList;public Menu(String name,int level){this.level = level;this.name = name;menuComponentList = new ArrayList<MenuComponent>();}@Overridepublic void add(MenuComponent menuComponent) {menuComponentList.add(menuComponent);}@Overridepublic void remove(MenuComponent menuComponent) {menuComponentList.remove(menuComponent);}@Overridepublic MenuComponent getChild(int i) {return menuComponentList.get(i);}@Overridepublic void print() {for (int i = 1; i < level; i++) {System.out.print("--");}System.out.println(name);for (MenuComponent menuComponent : menuComponentList) {menuComponent.print();}}
}

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

public class MenuItem extends MenuComponent {public MenuItem(String name,int level) {this.name = name;this.level = level;}@Overridepublic void print() {for (int i = 1; i < level; i++) {System.out.print("--");}System.out.println(name);}
}

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

组合模式的分类

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

  • 透明组合模式

透明组合模式中,抽象根节点角色中声明了所有用于管理成员对象的方法,比如在示例中 `MenuComponent` 声明了 `add`、`remove` 、`getChild` 方法,这样做的好处是确保所有的构件类都有相同的接口。透明组合模式也是组合模式的标准形式。

透明组合模式的缺点是不够安全,因为叶子对象和容器对象在本质上是有区别的,叶子对象不可能有下一个层次的对象,即不可能包含成员对象,因此为其提供 add()、remove() 等方法是没有意义的,这在编译阶段不会出错,但在运行阶段如果调用这些方法可能会出错(如果没有提供相应的错误处理代码)

  • 安全组合模式

在安全组合模式中,在抽象构件角色中没有声明任何用于管理成员对象的方法,而是在树枝节点 `Menu` 类中声明并实现这些方法。安全组合模式的缺点是不够透明,因为叶子构件和容器构件具有不同的方法,且容器构件中那些用于管理成员对象的方法没有在抽象构件类中定义,因此客户端不能完全针对抽象编程,必须有区别地对待叶子构件和容器构件。

优点

  • 组合模式可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次,它让客户端忽略了层次的差异,方便对整个层次结构进行控制。
  • 客户端可以一致地使用一个组合结构或其中单个对象,不必关心处理的是单个对象还是整个组合结构,简化了客户端代码。
  • 在组合模式中增加新的树枝节点和叶子节点都很方便,无须对现有类库进行任何修改,符合“开闭原则”。
  • 组合模式为树形结构的面向对象实现提供了一种灵活的解决方案,通过叶子节点和树枝节点的递归组合,可以形成复杂的树形结构,但对树形结构的控制却非常简单。

使用场景

组合模式正是应树形结构而生,所以组合模式的使用场景就是出现树形结构的地方。比如:文件目录显示,多级目录呈现等树形结构数据的操作。

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

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

相关文章

开始尝试从0写一个项目--前端(三)

器材管理板块 添加器材管理导航 src\views\home\Home.vue src\router\index.js src\views\equipment\Equipment.vue <template><div>hello!</div></template> 测试 搜索导航分页查询 src\views\equipment\Equipment.vue <template><div&…

Discourse 备份和恢复中有关附件的问题

下面的这个问题是在官方论坛上网友问的内容是&#xff1a; 我想问一下&#xff0c;备份和附件分别挂载了不同的S3 备份的时候会把附件的S3里面的内容也都一起备份了吗&#xff1f;如果不选择包含上传的图片和附件&#xff0c;那么恢复备份的时候&#xff0c;附件用的S3里面的内…

【区块链+绿色低碳】雄韬智慧锂电储能管理系统 | FISCO BCOS应用案例

雄韬智慧锂电储能管理系统&#xff08;Energy Management System&#xff0c;以下简称 EMS&#xff09;是雄韬集团响应国家实现新型电力 系统建设&#xff0c;助力“碳达峰、碳中和”目标而自主开发的创新智慧锂电储能系统。 系统采用了 FISCO BCOS 联盟链&#xff0c;融合了物…

昇思25天学习打卡营第9天 |昇思MindSpore 基于MindNLP+MusicGen生成自己的个性化音乐

一、MusicGen 模型原理 MusicGen 是由 Meta AI 的 Jade Copet 等人提出的基于单个语言模型&#xff08;LM&#xff09;的音乐生成模型。它能够依据文本描述或音频提示生成高质量的音乐样本&#xff0c;相关研究成果可参考论文《Simple and Controllable Music Generation》。 …

Java 8 中 20 个高频面试题及答案

文章目录 前言20 道高频题问题 1&#xff1a;给定一个整数列表&#xff0c;使用 Stream 函数找出列表中所有的偶数&#xff1f;问题 2&#xff1a;给定一个整数列表&#xff0c;使用 Stream 函数找出所有以 1 开头的数字&#xff1f;问题 3&#xff1a;如何使用 Stream 函数在给…

【C++题解】1782. 字符图形2-星号倒直角

问题&#xff1a;1782. 字符图形2-星号倒直角 类型&#xff1a;嵌套循环、图形输出 题目描述&#xff1a; 打印字符图形。 输入&#xff1a; 一个整数&#xff08; 0<n<10 &#xff09;。 输出&#xff1a; 一个字符图形。 样例&#xff1a; 输入&#xff1a; 3…

Python --NumPy库基础方法(2)

NumPy Numpy(Numerical Python) 是科学计算基础库&#xff0c;提供大量科学计算相关功能&#xff0c;比如数据统计&#xff0c;随机数生成等。其提供最核心类型为多维数组类型&#xff08;ndarray&#xff09;&#xff0c;支持大量的维度数组与矩阵运算&#xff0c;Numpy支持向…

深度学习中的损失函数和网络优化方法

深度学习中的损失函数和网络优化方法是构建和训练神经网络的核心部分。损失函数用于衡量模型预测值与实际值之间的差距&#xff0c;而网络优化方法用于调整模型参数以最小化损失函数&#xff0c;从而提升模型的性能。以下是对损失函数和网络优化方法的详细介绍。 损失函数 损…

微信Android一面凉经(2024)

微信Android一面凉经(2024) 笔者作为一名双非二本毕业7年老Android, 最近面试了不少公司, 目前已告一段落, 整理一下各家的面试问题, 打算陆续发布出来, 供有缘人参考。今天给大家带来的是《微信Android一面凉经(2024)》。 面试职位: 微信-客户端开发工程师-基础功能(广州) And…

D语言

提起编程语言&#xff0c;相信大家第一时间想到的肯定都是C,但是相信资历较大的程序员们都不会忘记D语言 D语言自 1999 年发布至今已发展了 20 年 它既拥有 Java 那样强大的表现力 又具有 C 相当的性能 却因为 2.x 版本破坏性升级 导致社区大量核心开发者将其放弃。 2010 年——…

Unity GameObject: 构建游戏世界的基本元素

在Unity这个游戏开发平台中&#xff0c;GameObject是构成游戏世界的基础构件。无论是角色、敌人、道具还是环境元素&#xff0c;它们都是GameObject的不同表现形式。本文将深入探讨Unity中GameObject的概念、重要性以及如何使用它们来构建和丰富你的游戏世界。 GameObject简介…

Ubuntu22.04系统安装nodejs 14 保姆级教程

下载软件包 从NodeSource 的官方源下载并安装 Node.js 14.x 版本的软件包&#xff0c;适用于 Debian 和 Ubuntu 系统&#xff1a; curl -sL https://deb.nodesource.com/setup_14.x | sudo -E bash - 更新软件源 更新软件源 sudo apt-get update 下载bodejs14 下载nodejs14 sud…

双指针算法的实现(三题详解)

这是C算法基础-基础算法专栏的第十五篇文章&#xff0c;专栏详情请见此处。 ps&#xff1a;转眼间暑假已过半&#xff0c;我在这段时间也积累了很多文章&#xff0c;所以到开学&#xff08;9月1日&#xff09;为止&#xff0c;每个周我将会在周三和周六发文章(o&#xff9f;▽&…

Springboot项目的行为验证码AJ-Captcha(源码解读)

目录 前言1. 复用验证码2. 源码解读2.1 先走DefaultCaptchaServiceImpl类2.2 核心ClickWordCaptchaServiceImpl类 3. 具体使用 前言 对于Java的基本知识推荐阅读&#xff1a; java框架 零基础从入门到精通的学习路线 附开源项目面经等&#xff08;超全&#xff09;【Java项目…

针对datax-web 中Swagger UI接口未授权访问

application.yml 添加以下配置 实现访问doc.html 以及/v2/api-docs 接口时需要进行简单的校验 swagger:basic:enable: trueusername: adminpassword: 12345 配置重启后再进行相关访问则需要输入用户名和密码

Ubuntu 24.04 LTS Noble安装 FileZilla Server

FileZilla Server 是一款使用图形用户界面快速创建 FTP 服务器的软件。它有助于测试需要 FTP 服务器功能的各种项目。虽然早期的 FileZilla FTP 服务器仅适用于 Windows 和 macOS&#xff0c;但现在我们也可以在 Linux&#xff08;例如 Ubuntu 24.04&#xff09;上安装 FileZil…

c++红黑树,插入公式

概念 红黑树&#xff0c;是一种二叉搜索树&#xff0c;但在每个结点上增加一个存储位表示结点的颜色&#xff0c;可以是Red或 Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制&#xff0c;红黑树确保没有一条路 径会比其他路径长出俩倍&#xff0c;因而是接近…

C++ STL set_symmetric_difference

一&#xff1a;功能 给定两个集合A&#xff0c;B&#xff1b;求出两个集合的对称差&#xff08;只属于其中一个集合&#xff0c;而不属于另一个集合的元素&#xff09;&#xff0c;即去除那些同时在A&#xff0c;B中出现的元素。 二&#xff1a;用法 #include <vector>…

【前端手写代码】手写Object.create

思路&#xff1a;将传入的对象作为原型 // 思路&#xff1a;将传入的对象作为原型 function create(obj) {function F() { }F.prototype objreturn new F() }

Go 语言单例化利器 Once

Once 可以用来执行仅仅执行一次的动作,常常被应用于单个对象的初始化场景。 1. Once 的使用方法 sync.Once 只暴露了一个方法 Do,你可以多次调用 Do 方法,但是只有第一次调用 Do 方法时参数 f 才会执行,这里的 f 是一个无参数、无返回值的函数。 func (o *Once) do(f func…