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

news2025/3/8 11:20:49

前言

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

一、 处理树形结构的挑战

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

// 文件类
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 和文件 2
        folder2.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);
        }
    
        // 文件不支持添加操作,因此抛出异常
        @Override
        public void add(FileSystemComponent component) {
            throw new UnsupportedOperationException("Cannot add to a file.");
        }
    
        // 文件不支持移除操作,因此抛出异常
        @Override
        public void remove(FileSystemComponent component) {
            throw new UnsupportedOperationException("Cannot remove from a file.");
        }
    
        // 显示文件名称
        @Override
        public void displayStructure() {
            System.out.println("File: " + name);
        }
    }
    
  3. 创建容器构件(Composite): 接下来,我们创建 Folder 类,它也是 FileSystemComponent 的实现,可以包含叶子构件或其他容器构件。

    // 容器构件:可以包含叶子构件或其他容器构件的文件夹
    public class Folder extends FileSystemComponent {
        // 文件夹构造函数
        public Folder(String name) {
            super(name);
        }
    
        // 添加子部件到文件夹
        @Override
        public void add(FileSystemComponent component) {
            children.add(component);
        }
    
        // 从文件夹移除子部件
        @Override
        public void remove(FileSystemComponent component) {
            children.remove(component);
        }
    
        // 显示文件夹及其子部件的结构
        @Override
        public 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 和文件 2
            folder2.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.coloradmin.cn/o/1789404.html

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

相关文章

实习面试题(答案自敲)、

1、为什么要重写equals方法&#xff0c;为什么重写了equals方法后&#xff0c;就必须重写hashcode方法&#xff0c;为什么要有hashcode方法&#xff0c;你能介绍一下hashcode方法吗&#xff1f; equals方法默认是比较内存地址&#xff1b;为了实现内容比较&#xff0c;我们需要…

RF自动化框架-环境搭建

一、RobotFrame框架简介 &#xff08;1&#xff09;RobotFramework简称&#xff1a;RF框架&#xff0c;Robotframework, 采用PO设计模式(page objeck&#xff0c; 页面即对象&#xff0c;将一个实现过程分成不同层次&#xff0c;其实就是一个分层与封装的模式) &#xff08;2…

一键设置常用纸张和页面边距-Word插件-大珩助手

Word大珩助手是一款功能丰富的Office Word插件&#xff0c;旨在提高用户在处理文档时的效率。它具有多种实用的功能&#xff0c;能够帮助用户轻松修改、优化和管理Word文件&#xff0c;从而打造出专业而精美的文档。 【新功能】常用纸张和常用边距 1、一键设定符合中国人常用…

非递归实现快排排序及归并排序(尾篇)

1.快速排序&#xff08;双指针实现&#xff09; 2.非递归实现快排 3.递归实现归并排序 4.非递归实现归并排序 5.总代码 1.快速排序&#xff08;双指针实现&#xff09; 俩有个指针一前一后的排放着&#xff0c;cur先走并且去找比kye对应值小的数组值&#xff0c;一旦找到后…

我国液碱产量逐渐增长 行业集中度有望不断提升

我国液碱产量逐渐增长 行业集中度有望不断提升 液碱是由氢氧化钠&#xff08;NaOH&#xff09;、氢氧化钾&#xff08;KOH&#xff09;等化合物以及水组成的一种碱性化合物。液碱的相对分子质量为40.00&#xff0c;密度为1.318g/cm&#xff0c;在常温常压下多表现为一种无色、无…

萨科微的“Slkor”和金航标“Kinghelm”

宋仕强说&#xff0c;我国的科学研究和先进技术&#xff0c;与先进国家相比还有差距&#xff0c;这一点还体现在社会生产效率和人均GDP上面。我们只有抓住科技进步的风口如人工智能&#xff08;AI&#xff09;&#xff0c;再加上公司内部的研发和管理等环节的微创新&#xff0c…

ai怎么导出jpg?让我告诉你答案【详】

在设计和创意工作中&#xff0c;Adobe Illustrator&#xff08;AI&#xff09;是一款不可或缺的工具。然而&#xff0c;当我们将设计作品导出为JPG格式时&#xff0c;可能会遇到一些问题。ai怎么导出jpg&#xff1f;如何确保导出的JPG图片保持高质量&#xff1f;接下来&#xf…

原子阿波罗STM32F429程序的控制器改为STM32F407

以前&#xff0c;学习原子的探索者开发板&#xff0c;有STM32F407ZGT6开发板&#xff0c;现在想学习阿波罗开发板&#xff0c;但手头没有F429开发板&#xff0c;于是&#xff0c;想把STM32F429芯片替换为STM32F407芯片&#xff0c;本以为没有什么难度&#xff0c;但是替换后发下…

Mysql:通过一张表里的父子级,递归查询并且分组分级

表&#xff1a;gc_jzst_single_base 需求&#xff1a;要求返回这张表里符合条件的数据&#xff0c;且有父子级关系的&#xff0c;展示为同一组且分级&#xff0c;给后续业务调用 代码 WITH RECURSIVE t1 AS (SELECTsingle_id,old_build_single_id,single_name,bulid_code,1 A…

ArcGIS中几个好用的空间分析工具

ArcGIS是一款经典的GIS应用&#xff0c;其空间分析能力很强&#xff0c;有着丰富的空间分析工具。今天&#xff0c;我们一起来了解几个好用的空间分析工具的功用及操作。 注&#xff1a;演示版本为ArcMap10.4.1 1.方向分布&#xff08;标准差椭圆&#xff09; 路径&#xff…

三.网络编程套接字_TCP

一.序言 在上一章中&#xff0c;我们已经实现了用udp来实现网络编程&#xff0c;这一节我们用tcp来实现网络编程&#xff0c;通过对比两者编写过程的区别&#xff0c;来加深对udp,tcp的理解&#xff01; (两者其实差别不大&#xff01;有了udp的基础&#xff0c;学习起来tcp会…

MongoDB~存储引擎了解

存储引擎 存储引擎是一个数据库的核心&#xff0c;主要负责内存、磁盘里数据的管理和维护。 MongoBD的优势&#xff0c;在于其数据模型定义的灵活性、以及可拓展性。但不要忽略&#xff0c;其存储引擎也是插件式的存在&#xff0c;支持不同类型的存储引擎&#xff0c;使用不同…

C++学习笔记(22)——多态

目录 [TOC](目录) 比喻与理解1. 多态的概念2. 多态的定义及实现2.1多态的构成条件2.2 虚函数2.3虚函数的重写2.3.1 虚函数重写的两个例外&#xff1a;1. 协变(基类与派生类虚函数返回值类型不同)2. 析构函数的重写(基类与派生类析构函数的名字不同) 2.4 C11 override 和 final2…

【Git教程】(二十)外包长历史记录 — 概述及使用要求,执行过程及其实现,替代解决方案 ~

Git教程 外包长历史记录 1️⃣ 概述2️⃣ 使用要求3️⃣ 执行过程及其实现3.1 外包项目历史3.2 链接到当前活动版本库 Git 版本库会随着时间积累越来越大&#xff0c;会影响它的内存管理效率。通常在版本库中只有源 代码文件情况下&#xff0c;这点效率影响可以忽略不计。在现…

新火种AI|倒反天罡!美国名校斯坦福AI团队抄袭中国大模型

作者&#xff1a;一号 编辑&#xff1a;美美 中国大模型被抄袭&#xff0c;怎么不算是某种层面上的国际认可呢&#xff1f; 5月29日&#xff0c;斯坦福大学的一个AI研究团队发布了一个名为「Llama3V」的模型&#xff0c;号称只要 500 美元就能训练出一个 SOTA 多模态模型&am…

精酿啤酒新风尚,FENDI CLUB盛宴启幕,品质生活触手可及

随着现代人对生活品质的追求日益提升&#xff0c;精酿啤酒作为一种新兴的生活方式&#xff0c;正逐渐引领潮流。在这个背景下&#xff0c;FENDI CLUB的盛宴盛大开启&#xff0c;为广大消费者带来了一场别具一格的品质生活体验。 一、精酿啤酒的崛起 精酿啤酒以其独特的口感、…

手机卡不缴纳违约金就不给注销?实用的处理方法大全!

我手机卡都不用了&#xff0c;为何不能注销&#xff1f;而且要缴纳违约金&#xff1f;简直是无法无天&#xff01;小编在回复粉丝问题的时候&#xff0c;经常遇到这种情况&#xff0c;现在就给大家系统整理下如何处理这个问题&#xff0c;希望能帮助到大家&#xff01; 在处理不…

段子照进现实!裁员裁到大动脉,理想被传召回被裁员工…?

你一定看过类似这样的段子吧&#xff01;「公司高层换血&#xff0c;各个部门丢裁了个遍&#xff0c;终于要对财务下手&#xff0c;财务总监走之前&#xff0c;让公司补了六百万税」 还有类似这样的&#xff1a;「某公司裁员把一个销售主管裁了&#xff0c;那销售上午刚谈了个1…

vue动态加载组件import引入组件找不到组件(Error: Cannot find module)

更多ruoyi-nbcio功能请看演示系统 gitee源代码地址 前后端代码&#xff1a; https://gitee.com/nbacheng/ruoyi-nbcio 演示地址&#xff1a;RuoYi-Nbcio后台管理系统 http://218.75.87.38:9666/ 更多nbcio-boot功能请看演示系统 gitee源代码地址 后端代码&#xff1a; h…

【杂谈】AIGC之Stable Diffusion:AI绘画的魔法

Stable Diffusion&#xff1a;AI绘画的魔法 引言 在AI的世界里&#xff0c;Stable Diffusion就像一位魔法师&#xff0c;它能够将我们脑海中的幻想&#xff0c;用画笔一一描绘出来。今天&#xff0c;就让我们一探这位魔法师的奥秘&#xff0c;看看它是如何从无到有&#xff0…