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

前言

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

一、 处理树形结构的挑战

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

// 文件类
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.mzph.cn/pingmian/22397.shtml

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

相关文章

【前端技术】 ES6 介绍及常用语法说明

&#x1f604; 19年之后由于某些原因断更了三年&#xff0c;23年重新扬帆起航&#xff0c;推出更多优质博文&#xff0c;希望大家多多支持&#xff5e; &#x1f337; 古之立大事者&#xff0c;不惟有超世之才&#xff0c;亦必有坚忍不拔之志 &#x1f390; 个人CSND主页——Mi…

使用wheelnav.js构建酷炫的动态导航菜单

目录 前言 一、WheelNav是什么 1、项目地址 2、关于开源协议 3、相关目录介绍 二、如何使用wheelnav.js 1、新建html页面 2、设置style样式 3、创建展示元素实现动态导航 三、参数即方法介绍 1、参数列表 2、运行方法 3、实际成果 四、总结 前言 用户体验永远是一…

玩转盲盒潮流:从0到1搭建小程序平台

玩转盲盒潮流并搭建一个从0到1的小程序平台来创作内容是一个充满挑战但有趣的过程。以下是一个步骤指南&#xff0c;帮助你实现这一目标&#xff1a; 1. 市场调研与定位 了解盲盒市场&#xff1a;研究当前盲盒市场的趋势、消费者喜好和成功案例。确定目标用户&#xff1a;明确…

软件质量保障——三、四

三、黑盒测试 1.黑盒测试概述 1.1 如何理解黑盒测试&#xff1f; 1.2 黑盒测试有什么特点&#xff1f; 1.3 如何实施黑盒测试&#xff1f; 2. 黑盒测试用例设计和生成方法&#xff08;这里还是要自己找题做&#xff09; 2.1 等价类划分法 步骤&#xff1a; 1.选择划分准…

BI平台概述

随着数字化浪潮的推进&#xff0c;企业对于数据驱动决策的需求日益增长。纷享销客作为一款领先的CRM平台&#xff0c;一直致力于帮助企业实现销售管理的高效与智能。纷享销客一体化BI智能分析平台作为CRM平台中的重要一环&#xff0c;旨在为企业提供更加全面、深入的数据分析能…

HBuilderX编写APP一、获取token

一、新建项目 二、从onenet获取key.js 1、下载之后的压缩包&#xff0c;解压2、关键就是找到key.js 3、将这个key.js复制到刚才的目录下面去 4、这个key.js文件就是生成token的代码 5、只要调用createCommonToken(params)这个函数&#xff0c;就可以实现生成token了 其中onload…

Java多线程核心工具类

1.Thread类&#xff1a;代表一个线程。你可以通过继承Thread类或实现Runnable接口来创建线程。 2.Executor框架&#xff1a;java.util.concurrent.Executors和java.util.concurrent.Executor接口提供了一种创建和管理线程池的方法&#xff0c;可以减少在创建和销毁线程上的开销…

【TB作品】msp430g2553单片机,OLED,PCF8591,ADC,DAC

硬件 OLED PCF8591 /** OLED* VCC GND* SCL接P2^0* SDA接P2^1*//** PCF8591* VCC GND* SCL接P1^4* SDA接P1^5*//* 板子上按键 P1.3 *//* 单片机ADC输入引脚 P1.1 *//* 说明&#xff1a;将PCF8591的DAC输出接到单片机ADC输入引脚 P1.1&#xff0c;单片机采集电压并显示 */功能…

Docker run 命令常用参数详解

Docker run 命令提供了丰富的参数选项&#xff0c;用于配置容器的各种设置。以下是docker run命令的主要参数详解&#xff0c; 主要参数详解 后台运行与前台交互 -d, --detach: 在后台运行容器&#xff0c;并返回容器ID。-it: 分配一个伪终端&#xff08;pseudo-TTY&#xff0…

RGB转LAB,HSV

Excel如下 目标 代码&#xff08;改下两个地址就可以&#xff09; import pandas as pd import colorspacious import colorsys# 读取Excel文件 df pd.read_excel(未分类output.xlsx)# 定义RGB到LAB和HSV的转换函数 def rgb_to_lab(rgb):lab colorspacious.cspace_convert(r…

Layui:一款强大的前端UI框架

随着互联网技术的快速发展&#xff0c;前端技术也在不断更新和演进。前端工程师们面临着越来越多的挑战&#xff0c;需要在短时间内构建出高质量、高效率的网页应用。为了提高开发效率和降低开发难度&#xff0c;许多前端UI框架应运而生。在这些框架中&#xff0c;Layui凭借其优…

Git-lfs入门使用教程

在备份我的毕设到github私有库的时候&#xff0c;发现git对于单文件大于100MB的会限制上传&#xff0c;一番折腾一下发现了git-lfs [Git LFS&#xff08;Large File Storage,大文件存储&#xff09;是 Github 开发的一个Git 的扩展&#xff0c;用于实现 Git 对大文件的支持]。 …

揭秘Linux启动的层层面纱,一文看懂从黑屏到界面的精彩之旅

从按下开机键到Linux系统界面显示&#xff0c;这中间究竟经历了怎样的过程?本文将为您一一揭开Linux启动的神秘面纱&#xff0c;详细剖析每个环节的工作原理&#xff0c;让你从内核出生到系统服务启动&#xff0c;一路见证这个过程的壮阔与精彩。 一、概述 Linux系统的启动过…

【场景题】如何排查CPU偏高的问题

为了解决CPU偏高的问题&#xff0c;我们首先看一下每一个进程的CPU占用情况&#xff0c;使用命令Top 可以看见是进程id为2266的进程里面的java程序&#xff0c;占用了CPU90%使用情况 所以我们需要找到是哪一个代码导致的这样的情况&#xff0c;由于代码是线程执行的&#xff…

测绘GIS和遥感领域比较好的公众号有哪些

测绘GIS和遥感领域&#xff0c;微信公众号作为信息传播和知识分享的重要渠道&#xff0c;为从业者提供了一个快速获取行业动态、技术进展和职业发展机会的平台。分享一些在测绘GIS和遥感领域表现突出的公众号推荐&#xff1a; 1. 慧天地&#xff1a;慧天地是一个知名的测绘公众…

【数据结构】穿梭在二叉树的时间隧道:顺序存储的实现

专栏引入 哈喽大家好&#xff0c;我是野生的编程萌新&#xff0c;首先感谢大家的观看。数据结构的学习者大多有这样的想法&#xff1a;数据结构很重要&#xff0c;一定要学好&#xff0c;但数据结构比较抽象&#xff0c;有些算法理解起来很困难&#xff0c;学的很累。我想让大家…

CDN(Content Delivery Network)内容分发网络原理、组成、访问过程、动静态加速、作用详解

CDN简介 什么是CND CDN&#xff08;Content Delivery Network&#xff09;的缩写&#xff0c;是一种利用分布式节点技术&#xff0c;在全球部署服务器&#xff0c;即时地将网站、应用视频、音频等静态或动态资源内容分发到用户所在的最近节点&#xff0c;提高用户访问这些内容…

PDF分页处理:技术与实践

引言 在数字化办公和学习中&#xff0c;PDF文件因其便携性和格式稳定性而广受欢迎。然而&#xff0c;处理大型PDF文件时&#xff0c;我们经常需要将其拆分成单独的页面&#xff0c;以便于管理和分享。本文将探讨如何使用Python编程语言和一些流行的库来实现PDF文件的分页处理。…

管道液位传感器在蒸汽清洗机的应用

管道光电液位传感器采用了先进的光学感应原理&#xff0c;无需机械运动&#xff0c;具有长久的寿命和稳定的性能。与传统的机械式和电容式传感器相比&#xff0c;管道光电液位传感器具有更高的精度和可靠性&#xff0c;解决了机械式传感器容易卡死失效和电容式传感器感度衰减的…

数据结构之初始泛型

找往期文章包括但不限于本期文章中不懂的知识点&#xff1a; 个人主页&#xff1a;我要学编程(ಥ_ಥ)-CSDN博客 所属专栏&#xff1a;数据结构&#xff08;Java版&#xff09; 目录 深入了解包装类 包装类的由来 装箱与拆箱 面试题 泛型 泛型的语法与使用…