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

前言

在软件开发中,设计模式是一种被广泛应用的解决问题的方法论。其中,结构性设计模式是一类特别重要的模式,它们用于处理类或对象之间的组合关系,其中之一就是组合模式。组合模式允许客户端统一对待单个对象和对象的组合,从而简化了代码的复杂性,增强了代码的灵活性和可维护性。

一、 处理树形结构的挑战

场景假设:我们需要开发一个文件系统。它包含文件和文件夹,文件夹中又可以包含其他文件或文件夹。

// 文件类
class File {String name;File(String name) {this.name = name;}void display() {System.out.println("File: " + name);}
}// 文件夹类,可以包含文件和其他文件夹
class Folder {String name;List<File> files;List<Folder> folders;Folder(String name) {this.name = name;this.files = new ArrayList<>();this.folders = new ArrayList<>();}void addFile(File file) {files.add(file);}void addFolder(Folder folder) {folders.add(folder);}void displayContents() {System.out.println("Folder: " + name);for (File file : files) {file.display();}for (Folder folder : folders) {folder.displayContents();}}
}public class Main {public static void main(String[] args) {File file1 = new File("file1.txt");File file2 = new File("file2.txt");Folder folder1 = new Folder("folder1");Folder folder2 = new Folder("folder2");folder1.addFile(file1);folder2.addFile(file2);folder2.addFolder(folder1);// 显示文件夹 2 的内容,它包含文件夹 1 和文件 2folder2.displayContents();// 这个设计不利于扩展,如果我们想要添加新类型的文件系统元素,比如链接,我们需要修改 Folder 类// 这违反了开闭原则,并且使得代码难以维护和扩展}
}

上面的示例中,我们创建了两个类:File 和 Folder。File 类代表文件系统中的文件,而 Folder 类代表可以包含文件和其他文件夹的文件夹。这种设计的问题在于它不够灵活,难以扩展。例如,如果我们想要添加一个新的文件系统元素,如链接,我们需要修改 Folder 类来支持这种新类型的元素。这违反了开闭原则,即软件实体应该对扩展开放,对修改关闭。

除了上述问题,还存在以下问题:

  1. 代码重复:我们需要为文件和文件夹编写不同的处理代码,这导致了代码重复。
  2. 难以维护:如果文件系统的结构发生变化,比如添加新类型的元素,我们需要修改现有的代码,这使得维护变得困难。
  3. 扩展性差:当前的设计不允许灵活地添加新类型的文件系统元素,限制了系统的扩展性。

二、组合模式

组合模式是一种结构型设计模式,旨在将对象组合成树形结构以表示“部分-整体”的层次结构。这种模式用于将对象组织成树形结构,以表示“部分-整体”的层次关系,使得客户端可以统一处理单个对象和对象的组合。

在组合模式中,有两种主要类型的对象:

  1. 叶子节点(Leaf):表示树中的最终节点,它没有子节点。
  2. 复合节点(Composite):表示树中的分支节点,它可以包含其他子节点,即可以是叶子节点,也可以是复合节点。

三、组合模式的核心组成

组合模式由三个关键角色组成:

  1. 组件(Component):是组合中所有对象的共同接口,客户端通过这个接口操作组合中的对象。
  2. 叶子(Leaf):表示树中的叶子节点,它实现了组件接口。
  3. 复合(Composite):表示树中的复合节点,它实现了组件接口,并拥有子组件。

在这里插入图片描述

这里,Component 是抽象基类,定义了操作、添加子节点、删除子节点以及获取子节点的抽象方法。Leaf 类表示树结构中的叶子节点,它没有子节点。Composite 类表示树结构中的复合节点,它可以包含子节点,并且实现了对子节点的操作方法。

四、运用组合模式

场景假设: 我们一个文件系统,其中包含文件和文件夹。这个文件系统需要能够以统一的方式处理单个文件和包含多个文件或子文件夹的文件夹。

  1. 定义抽象构件(Component): 首先,我们创建一个抽象类或接口 FileSystemComponent,它包含了管理子部件的公共接口,如添加(add)、删除(remove)和显示结构(displayStructure)子部件。

    // 抽象构件:定义了文件系统中所有对象共有的接口
    public abstract class FileSystemComponent {protected String name;// 子部件列表,用于存储文件或文件夹protected List<FileSystemComponent> children;// 构造函数初始化文件系统组件的名称public FileSystemComponent(String name) {this.name = name;this.children = new ArrayList<>();}// 添加子部件的方法public abstract void add(FileSystemComponent component);// 移除子部件的方法public abstract void remove(FileSystemComponent component);// 显示结构的方法,用于输出组件结构public abstract void displayStructure();
    }
    
  2. 创建叶子构件(Leaf): 然后,我们实现 FileSystemComponent 接口来创建 File 类,这是树形结构中的末端对象,没有子部件。

    // 叶子构件:实现了抽象构件的操作,代表没有子部件的文件
    public class File extends FileSystemComponent {// 文件构造函数public File(String name) {super(name);}// 文件不支持添加操作,因此抛出异常@Overridepublic void add(FileSystemComponent component) {throw new UnsupportedOperationException("Cannot add to a file.");}// 文件不支持移除操作,因此抛出异常@Overridepublic void remove(FileSystemComponent component) {throw new UnsupportedOperationException("Cannot remove from a file.");}// 显示文件名称@Overridepublic void displayStructure() {System.out.println("File: " + name);}
    }
    
  3. 创建容器构件(Composite): 接下来,我们创建 Folder 类,它也是 FileSystemComponent 的实现,可以包含叶子构件或其他容器构件。

    // 容器构件:可以包含叶子构件或其他容器构件的文件夹
    public class Folder extends FileSystemComponent {// 文件夹构造函数public Folder(String name) {super(name);}// 添加子部件到文件夹@Overridepublic void add(FileSystemComponent component) {children.add(component);}// 从文件夹移除子部件@Overridepublic void remove(FileSystemComponent component) {children.remove(component);}// 显示文件夹及其子部件的结构@Overridepublic void displayStructure() {System.out.println("Folder: " + name);for (FileSystemComponent component : children) {component.displayStructure();}}
    }
    
  4. 客户端使用: 最后,客户端代码可以统一对待单个对象和组合对象,使得用户对单个对象和组合对象的使用具有一致性。

    // 客户端使用示例
    public class FileSystemClient {public static void main(String[] args) {// 创建文件File file1 = new File("file1.txt");File file2 = new File("file2.txt");// 创建文件夹,并添加文件Folder folder1 = new Folder("folder1");Folder folder2 = new Folder("folder2");folder1.add(file1);folder2.add(file2);folder2.add(folder1);// 显示文件夹 2 的内容,它包含文件夹 1 和文件 2folder2.displayStructure();}
    }
    

通过上述的组合模式,我们可以实现:

  1. 统一接口:File 和 Folder 都实现了 FileSystemComponent 接口,这意味着客户端代码可以以相同的方式处理它们。
  2. 递归结构:Folder 可以包含其他 Folder 对象,这允许我们创建一个递归的树形结构,反映了文件系统的真实层次结构。
  3. 易于扩展:如果我们想要添加新类型的文件系统元素,我们只需要创建一个新的类,继承自 FileSystemComponent,并实现必要的方法。我们不需要修改现有的 Folder 类。

五、组合模式的应用场景

组合模式适用于以下几种场景:

  1. 图形用户界面(GUI)库: GUI 库通常使用组合模式来构建用户界面元素的层次结构。例如,窗口、面板、按钮和文本框可以作为容器对象,而文本、图像和复选框等用户界面元素可以作为叶子对象。
  2. 文件系统: 文件系统中的文件和目录可以被组织成一个树形结构。组合模式可以用于表示文件系统中的文件和目录,并且允许对它们进行统一的操作,如复制、移动和删除等。
  3. 组织架构: 组织架构中的部门、小组和员工等可以被组织成一个层次结构。组合模式可以用于表示组织架构,并且允许对不同层次的组织单元进行统一的管理。
  4. 菜单系统: 菜单系统通常具有多层次的菜单结构,例如菜单、子菜单和菜单项等。组合模式可以用于构建菜单系统,并且允许对菜单项和子菜单等组件进行统一的操作。
  5. 文件解析:在文件解析过程中,如 XML 文件、JSON 文件等,组合模式可以用来处理这些不同类型的文件,将它们表示为统一的对象,并提供一致的方法来读取和操作文件的内容。
  6. 电子设备: 电子设备通常具有复杂的层次结构,例如计算机系统中的硬件组件和软件模块等。组合模式可以用于表示这些层次结构,并且允许对不同层次的组件进行统一的控制和管理。

六、小结

组合模式是一种强大的设计模式,它提供了一种简单而灵活的方式来处理部分-整体层次关系。通过统一的接口和灵活的结构,组合模式使得系统更易于理解、扩展和维护。

推荐阅读

  1. Spring 三级缓存
  2. 深入了解 MyBatis 插件:定制化你的持久层框架
  3. Zookeeper 注册中心:单机部署
  4. 【JavaScript】探索 JavaScript 中的解构赋值
  5. 深入理解 JavaScript 中的 Promise、async 和 await

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

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

相关文章

使用cesiumLab使shp转为3dtlies

过程不做赘述&#xff0c;网上大把&#xff0c;说下注意事项。 1. 存储3DTiles 选项 若是打开则输出的文件为glb格式文件,因为glb文件好储存易传输跨平台。cesium可以使用但无法处理&#xff0c;例如改变颜色&#xff0c;改着色器等。若是不打开则输出的文件为bm3d格式文件,此…

二叉树—堆(C语言实现)

一、树的概念及结构 1.树的概念 树是一种非线性的数据结构&#xff0c;它是有n&#xff08;n > 0&#xff09;个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一颗倒挂的树&#xff0c;也就是说它是根朝上&#xff0c;而叶朝下。 ● 有一个特殊的结点…

C++设计模式-单例模式,反汇编

文章目录 25. 单例模式25.1. 饿汉式单例模式25.2. 懒汉式单例模式25.2.1. 解决方案125.2.2. 解决方案2 &#xff08;推荐写法&#xff09; 运行在VS2022&#xff0c;x86&#xff0c;Debug下。 25. 单例模式 单例即该类只能有一个实例。 应用&#xff1a;如在游戏开发中&#x…

搭建大型分布式服务(三十九)SpringBoot 整合多个kafka数据源-支持Aware模式

系列文章目录 文章目录 系列文章目录前言一、本文要点二、开发环境三、原项目四、修改项目五、测试一下五、小结 前言 本插件稳定运行上百个kafka项目&#xff0c;每天处理上亿级的数据的精简小插件&#xff0c;快速上手。 <dependency><groupId>io.github.vipjo…

算法第三天力扣第69题:X的平方根

69. x 的平方根 (可点击下面链接或复制网址进行做题) https://leetcode.cn/problems/sqrtx/https://leetcode.cn/problems/sqrtx/ 给你一个非负整数 x &#xff0c;计算并返回 x 的 算术平方根 。 由于返回类型是整数&#xff0c;结果只保留 整数部分 &#xff0c;小数部分将被…

SpringBoot的第二大核心AOP系统梳理

目录 1 事务管理 1.1 事务 1.2 Transactional注解 1.2.1 rollbackFor 1.2.2 propagation 2 AOP 基础 2.1 AOP入门 2.2 AOP核心概念 3. AOP进阶 3.1 通知类型 3.2 通知顺序 3.3 切入点表达式 execution切入点表达式 annotion注解 3.4 连接点 1 事务管理 1.1 事务…

【踏雪无痕的痕六】——数学中有意思的问题

一、背景介绍 提出一个问题往往比解决一个问题更有意义&#xff0c;因为提出一个问题相当于提出了一个思考问题的维度&#xff1b;而解决一个问题是沿着这个维度将已有的知识串起来的过程 三、过程 1.数人数你会吗&#xff1f; 小名再第10位&#xff0c;小李再第15位&#…

【源码】Spring Data JPA原理解析之Repository自定义方法命名规则执行原理(二)

Spring Data JPA系列 1、SpringBoot集成JPA及基本使用 2、Spring Data JPA Criteria查询、部分字段查询 3、Spring Data JPA数据批量插入、批量更新真的用对了吗 4、Spring Data JPA的一对一、LazyInitializationException异常、一对多、多对多操作 5、Spring Data JPA自定…

位运算算法

位运算是计算机中常用的一种运算方法&#xff0c;它直接对二进制数的位进行操作。位运算主要包括按位与&#xff08;&&#xff09;、按位或&#xff08;|&#xff09;、按位异或&#xff08;^&#xff09;、按位取反&#xff08;~&#xff09;、左移&#xff08;<<&a…

【云原生】Kubernetes----Ingress对外服务

目录 引言 一、K8S对外方式 &#xff08;一&#xff09;NodePort 1.作用 2.弊端 3.示例 &#xff08;二&#xff09;externalIPs 1.作用 2.弊端 3.示例 &#xff08;三&#xff09;LoadBalancer 1.作用 2.弊端 &#xff08;四&#xff09;Ingress 二、Ingress的…

ChatGPT-4o抢先体验

速度很快&#xff0c;结果很智能&#xff0c;支持多模态输入输出&#xff0c;感兴趣联系作者。 windows/linux/mac 客户端下载参考&#xff1a;https://github.com/lencx/Noi

dns域名解析服务和bond网卡

目录 dns域名解析服务 一、DNS 1、定义 2、以www.baidu.com为例 3、域名体系结构 4、DNS解析使用的协议和端口 5、dns域名解析的过程 6、dns解析的优先级 二、如何实现域名解析 1、域名解析 2、bind配置文件位置 &#xff08;一&#xff09;正向解析 &#xff08;…

IP协议1.0

基本概念&#xff1a; • 主机: 配有IP地址, 但是不进⾏路由控制的设备; • 路由器: 即配有IP地址, ⼜能进⾏路由控制; • 节点: 主机和路由器的统称; IP协议的报头 • 4位版本号(version): 指定IP协议的版本, 对于IPv4来说, 就是4. • 4位头部⻓度(header length): IP头部的⻓…

高并发系统限流原理

短时间内巨大的访问流量&#xff0c;我们如何让系统在处理高并发的同时还能保证自身系统的稳定性&#xff1f;估计有人会说&#xff0c;增加机器就可以了&#xff0c;因为我的系统架构设计就是按照分布式思想进行架构设计的&#xff0c;所以可以只需要增加机器就可以解决问题了…

计算机组成原理·考点知识点整理

根据往年考试题&#xff0c;对考点和知识点的一个整理。 校验编码 码距 一种编码的最小码距&#xff0c;其实就是指这种编码的码距。码距有两种定义&#xff1a; 码距所描述的对象含义 2 2 2 个特定的码其二进制表示中不同位的个数一种编码这种编码中任意 2 2 2 个合法编码的…

QT+FFmpeg+Windows开发环境搭建(加薪点)

01、Windows 环境搭建 FFMPEG官网:http://ffmpeg.org/ 02、下载4.2.1版本源码 源码:https://ffmpeg.org/releases/ffmpeg-4.2.1.tar.bz2 03、下载4.2.1编译好的文件 下载已经编译好的FFMPEG)(迅雷下载很快) 网址:https://ffmpeg.zeranoe.com/builds/ 32位下载地址:(迅雷…

【wiki知识库】05.分类管理模块--后端SpringBoot模块

&#x1f4dd;个人主页&#xff1a;哈__ 期待您的关注 目录 一、&#x1f525;今日目标 二、☀SpringBoot代码修改 1.使用逆向工程生成Category表结构 2. 新增CategoryQueryParam 3.新增CategorySaveParam 4.新增CategotyQueryVo 三、&#x1f916;新增分类管理的相关接口…

8. C#多线程基础概念

文章目录 一. 目标二. 技能介绍① 进程和线程② 为什么需要多线程③ C#实现多线程的方式④ 线程的操作(创建_终止_挂起_恢复) 一. 目标 进程和线程基本概念为什么需要多线程?C#实现多线程的方式?线程Thread的创建,终止,挂起和恢复? 二. 技能介绍 ① 进程和线程 什么是进程…

IO流,文件操作

参考 Java IO 基础知识总结 | JavaGuide 史上最骚最全最详细的IO流教程&#xff0c;没有之一&#xff01; - 宜春 - 博客园 零、io-流简介 IO 即 Input/Output&#xff0c;输入和输出。数据输入到计算机内存的过程即输入&#xff0c;反之输出到外部存储&#xff08;比如数据…

Microsoft Outlook Lite 引入短信功能

随着科技的不断进步&#xff0c;我们的沟通方式也在不断演变。微软最新推出的 Outlook Lite 应用&#xff0c;不仅为我们提供了一个轻量级的电子邮件管理工具&#xff0c;现在更是带来了一项令人兴奋的新功能——短信服务。 Outlook Lite&#xff1a;轻量级&#xff0c;功能全…