设计模式-组合模式(Composite Pattern)

1. 概念

  • 组合模式是一种结构型设计模式,它允许将对象组合成树状的层次结构,用来表示“整体-部分”的关系。

2. 原理结构图

原理图
在这里插入图片描述

  • 抽象角色(Component):这是组合模式的核心,它定义了树叶和树枝构件的公共接口,并可能提供一些默认行为。在透明式的组合模式中,它还声明了访问和管理子类的接口;而在安全式的组合模式中,这些管理工作由树枝构件完成。
  • 树叶角色(Leaf):这个角色代表了组合中的叶节点对象,它没有子节点,用于继承或实现抽象构件。简单来说,树叶构件是基本的对象,没有进一步分解的部分。
  • 树枝角色(Composite):这个角色是组合中的分支节点对象,它有子节点,同样用于继承和实现抽象构件。树枝构件的主要作用是存储和管理子部件,通常包含添加、删除、获取子节点的方法。

透明组合模式在这里插入图片描述

  • 透明组合模式的特点是Leaf 和 Composite 具有相同的接口。因此,无论客户端处理的是 Leaf 还是 Composite,都可以使用相同的接口。这使得客户端的操作更加简单和直观。然而,透明组合模式的一个潜在问题是,对于叶子节点来说,某些操作(如添加或删除子节点)是无意义的,这可能在运行时引发错误,除非有适当的错误处理机制。

安全组合模式
在这里插入图片描述

  • 安全组合模式的主要特点是,它将管理子构件的方法移到树枝构件中,使得抽象构件和树叶构件没有管理子对象的方法,从而避免了潜在的安全性问题。这种设计使得客户端在处理不同角色时能够明确区分,但对于客户端来说,可能需要针对不同类型的对象进行不同的操作,因此不够透明。

3. 代码示例

3.1 示例1:透明组合模式
  • 一个公司的组织结构,其中公司是一个复合对象,包含多个部门,而部门既可以包含其他部门(例如子部门),也可以包含员工(叶子节点)。
interface Component {  
    void operation();  
    void add(Component component);  
    void remove(Component component);  
    Component getChild(int index);  
    int getChildCount();  
    boolean isComposite();  
}
// 叶子节点:员工  
class Leaf implements Component {  
    private String name;  
  
    public Leaf(String name) {  
        this.name = name;  
    }  
  
    @Override  
    public void operation() {  
        System.out.println("Employee " + name + " is working.");  
    }  
  
    @Override  
    public void add(Component component) {  
        throw new UnsupportedOperationException("Cannot add to a leaf.");  
    }  
  
    @Override  
    public void remove(Component component) {  
        throw new UnsupportedOperationException("Cannot remove from a leaf.");  
    }  
  
    @Override  
    public Component getChild(int index) {  
        throw new UnsupportedOperationException("Leaf has no children.");  
    }  
  
    @Override  
    public int getChildCount() {  
        return 0;  
    }  
  
    @Override  
    public boolean isComposite() {  
        return false;  
    }  
}  
  
// 复合节点:部门  
class Composite implements Component {  
    private List<Component> children = new ArrayList<>();  
    private String name;  
  
    public Composite(String name) {  
        this.name = name;  
    }  
  
    @Override  
    public void operation() {  
        System.out.println("Department " + name + " is managing its resources.");  
        for (Component child : children) {  
            child.operation();  
        }  
    }  
  
    @Override  
    public void add(Component component) {  
        children.add(component);  
    }  
  
    @Override  
    public void remove(Component component) {  
        children.remove(component);  
    }  
  
    @Override  
    public Component getChild(int index) {  
        return children.get(index);  
    }  
  
    @Override  
    public int getChildCount() {  
        return children.size();  
    }  
  
    @Override  
    public boolean isComposite() {  
        return true;  
    }  
}

public class CompanyStructureDemo {  
    public static void main(String[] args) {  
        // 创建部门(复合节点)和员工(叶子节点)  
        Component hr = new Composite("HR Department");  
        Component it = new Composite("IT Department");  
        Component employee1 = new Leaf("John Doe");  
        Component employee2 = new Leaf("Jane Smith");  
        Component subDepartment = new Composite("Sub IT Department");  
        Component employee3 = new Leaf("Bob Johnson");  
  
        // 将员工添加到部门中  
        hr.add(employee1);  
        hr.add(employee2);  
  
        // 创建子部门,并将员工添加到子部门中  
        subDepartment.add(employee3);  
  
        // 将子部门添加到IT部门中  
        it.add(subDepartment);  
  
        // 执行操作  
        hr.operation();  
        it.operation();  
  
        // 移除操作(如果需要)  
        // hr.remove(employee1);  
  
        // 访问子节点(如果需要)  
        // Component child = hr.getChild(0);  
        // child.operation();  
    }  
}
  • 输出
Department HR Department is managing its resources.
Employee John Doe is working.
Employee Jane Smith is working.
Department IT Department is managing its resources.
Department Sub IT Department is managing its resources.
Employee Bob Johnson is working.
  • 这个案例展示了组织结构,其中部门可以包含其他部门或员工,而员工不能包含其他组件。通过这种方式,客户端可以一致地处理单个员工和整个部门,无需关心它们的具体类型。

3.2 示例2:安全组合模式
  • 文件系统案例
interface FileSystemElement {  
    void print(); // 打印文件系统元素  
}

class File implements FileSystemElement {  
    private String name;  
    private long size;  
  
    public File(String name, long size) {  
        this.name = name;  
        this.size = size;  
    }  
  
    @Override  
    public void print() {  
        System.out.println("文件: " + name + ", 大小: " + size + " 字节");  
    }  
}

class Directory implements FileSystemElement {  
    private String name;  
    private List<FileSystemElement> children = new ArrayList<>();  
  
    public Directory(String name) {  
        this.name = name;  
    }  
  
    @Override  
    public void print() {  
        System.out.println("目录: " + name);  
        for (FileSystemElement child : children) {  
            child.print(); // 递归打印子文件和子目录  
        }  
    }  
  
    // 安全组合模式特有的方法,用于添加子文件和子目录  
    public void add(FileSystemElement fileSystemElement) {  
        children.add(fileSystemElement);  
    }  
  
    // 安全组合模式特有的方法,用于移除子文件和子目录  
    public void remove(FileSystemElement fileSystemElement) {  
        children.remove(fileSystemElement);  
    }  
  
    // 隐藏子文件和子目录的访问,这是安全组合模式的关键点  
    // 这里没有提供直接访问子文件和子目录的方法  
}

public class FileSystemDemo {  
    public static void main(String[] args) {  
        // 创建文件  
        FileSystemElement file1 = new File("example.txt", 1024);  
        FileSystemElement file2 = new File("report.pdf", 5120);  
  
        // 创建目录  
        Directory docsDir = new Directory("docs");  
        docsDir.add(file1);  
  
        Directory imagesDir = new Directory("images");  
        imagesDir.add(new File("photo.jpg", 20480));  
  
        // 创建根目录  
        Directory rootDir = new Directory("/");  
        rootDir.add(docsDir);  
        rootDir.add(imagesDir);  
        rootDir.add(file2);  
  
        // 打印整个文件系统  
        rootDir.print();  
  
        // 客户端不能直接操作子文件和子目录,只能通过目录系统来完成  
        // 例如,添加文件需要首先找到对应的目录  
        // 然后通过目录提供的方法来添加  
        // Directory newDir = new Directory("newDir");  
        // rootDir.add(newDir);  
  
        // 同样,移除文件或目录也需要通过文件系统来完成  
        // rootDir.remove(docsDir);  
    }  
}
  • 将看到如下输出:
目录: /  
目录: docs  
文件: example.txt, 大小: 1024 字节  
目录: images  
文件: photo.jpg, 大小: 20480 字节  
文件: report.pdf, 大小: 5120 字节
  • 在这个安全组合模式的实现中,客户端可以打印整个文件系统或者子目录,但不能直接操作子文件和子目录。对于文件和目录的添加、删除和修改,客户端需要通过目录提供的 add 和 remove 方法来执行。这样,文件和目录的管理被封装在目录内部,提高了安全性并简化了客户端的使用。安全组合模式适用于那些需要限制对子对象直接访问的场景,如本例中的文件系统。

4. 优缺点

  • 主要作用
    • 将对象组合成树形结构以表示“部分-整体”的层次关系,它让客户端可以一致地对待单个对象和组合对象。
  • 优点
    • 定义层次结构:它允许你清晰地定义分层次的复杂对象,并表示对象的全部或部分层次。
    • 忽略层次差异:客户端可以忽略层次之间的差异,方便对整个层次结构进行控制。
    • 简化客户端代码:由于组合模式提供了统一的接口来处理单个对象和组合对象,因此可以减少客户端的代码复杂度。
    • 统一访问方式:用户可以通过一致的方式访问单个对象和组合对象,这使得接口对外显得透明,简化了用户的操作。
  • 缺点
    • 设计复杂性增加:组合模式使得设计变得更加复杂,因为需要处理不同层次的组件,包括叶子节点和容器节点。这要求开发者对整体结构有深入的理解,以正确实现和管理这些组件。
    • 管理困难:在组合模式中,容器对象可以包含其他容器对象,这种递归结构可能导致难以管理和维护,特别是在大型系统中。同时,对于组件的添加、删除和修改操作也可能变得复杂。
    • 性能开销:由于组合模式中的对象通常以树形结构存在,因此在进行一些操作时(如遍历树),可能会引入额外的性能开销,特别是在处理大型树结构时。
    • 客户端需要了解对象结构:虽然组合模式提供了统一的接口来操作组件,但客户端仍然需要了解组件的结构和类型(例如,区分叶子节点和容器节点),这可能会增加客户端代码的复杂性。

5. 应用场景

5.1 主要包括以下几个方面
  1. 树状数据结构:任何需要以树状结构组织数据的场景都可以使用组合模式,例如组织架构、目录结构等。

5.2 实际应用
  1. 文件系统和目录管理:文件系统可以被视为一个树形结构,其中目录作为容器,文件和子目录作为内容。使用组合模式,可以方便地创建、删除、移动和复制文件或目录,实现统一的文件和目录管理。
  2. 组织架构和部门管理:在一个企业或机构中,通常存在多个部门和员工,形成一定的层次结构。使用组合模式,可以构建灵活的组织架构模型,实现部门、岗位和员工的统一管理和操作,如计算总工资、获取某个部门下的所有员工等。
  3. 菜单和菜单项管理:在图形界面中,菜单通常包含多个菜单项,菜单项可以是子菜单或其他操作项。组合模式可以用于构建菜单的树形结构,实现菜单和菜单项的添加、删除、遍历等统一操作。

6. JDK中的使用

  • 在集合框架中,List、Set和Map等接口表示不同类型的集合,而它们的实现类(如ArrayList、HashSet、HashMap等)则提供了具体的实现。这些接口和类之间的关系形成了树形结构,其中接口作为抽象构件,而实现类作为具体的叶子构件或容器构件。通过这种结构,用户可以统一地使用这些集合类,而无需关心它们的具体实现细节。

7. 注意事项

  • 抽象层次的一致性:确保客户端对单个对象和组合对象的使用具有一致性。这意味着,无论是操作单个对象还是操作组合对象,客户端调用的接口应该是一样的。这有助于简化客户端代码,并增强系统的灵活性和可扩展性。
  • 递归处理:由于组合模式涉及树形结构,因此在处理组合对象时,通常需要递归地遍历整个树结构。在编写递归算法时,要特别注意避免无限递归和栈溢出等问题。
  • 安全性与完整性:在添加、删除或修改组合对象中的成员时,要确保操作的安全性和数据的完整性。例如,在删除成员时,要确保不会破坏树形结构的完整性;在添加成员时,要确保新成员与现有成员之间的关系正确无误。
  • 性能考虑:组合模式在处理大型树形结构时可能会带来性能问题。由于需要递归遍历整个树结构,如果树很大,那么处理时间可能会很长。因此,在设计系统时,要充分考虑性能因素,并考虑使用缓存、优化算法等方式来提高性能。
  • 扩展性:在设计组合模式时,要考虑到未来的扩展性。例如,如果将来需要添加新的操作或新的对象类型,系统应该能够灵活地适应这些变化。这可以通过使用接口、抽象类等方式来实现。
  • 封装性:组合模式中的内部实现细节应该被封装起来,以避免客户端直接访问和操作内部对象。这样可以提高系统的安全性和稳定性,并降低客户端代码的复杂性。
  • 避免过度使用:虽然组合模式具有很多优点,但并不意味着在所有情况下都应该使用它。过度使用组合模式可能会导致系统变得复杂且难以维护。因此,在决定是否使用组合模式时,要充分考虑实际需求和系统的特点。

8. 生成器模式 VS 组合模式 VS 装饰器模式

模式目的模式架构主要角色应用场景
建造者模式分步构建复杂对象指挥者,生成器构建具有复杂逻辑的对象
组合模式表示具有层次结构的对象组合类和叶子节点树形结构和递归结构
装饰器模式动态添加新功能抽象组件和装饰器功能组合和扩展

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

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

相关文章

跟TED演讲学英文:The inside story of ChatGPT‘s astonishing potential by Greg Brockman

The inside story of ChatGPT’s astonishing potential Link: https://www.ted.com/talks/greg_brockman_the_inside_story_of_chatgpt_s_astonishing_potential Speaker: Greg Brockman Date:April 2023 文章目录 The inside story of ChatGPTs astonishing potentialIntro…

第100+5步 ChatGPT文献复现:ARIMAX预测肺结核 vol. 5

基于WIN10的64位系统演示 一、写在前面 我们继续往下看&#xff0c;首先例行回顾文章&#xff1a; 《PLoS One》杂志的2023年一篇题目为《A comparative study of three models to analyze the impact of air pollutants on the number of pulmonary tuberculosis cases in …

zdpreact_antdesginpro 研究一下react里面比较流行的一个UI框架,开发后台管理系统

首先看一下最开始的代码&#xff1a; 这里面大部分的东西都可以删掉&#xff0c;比如README&#xff0c;只留下中文的那个就可以了。 之后看看README.md中介绍的特性。 特性 &#x1f4a1; TypeScript: 应用程序级 JavaScript 的语言&#x1f4dc; 区块: 通过区块模板快速…

LeetCode 热题 100 题解(二):双指针部分(2)| 滑动窗口部分(1)

题目四&#xff1a;接雨水&#xff08;No. 43&#xff09; 题目链接&#xff1a;https://leetcode.cn/problems/trapping-rain-water/description/?envTypestudy-plan-v2&envIdtop-100-liked 难度&#xff1a;困难 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&am…

[数据概念|数据技术]智能合约如何助力数据资产变现

“ 区块链上数据具有高可信度&#xff0c;智能合约将区块链变得更加智能化&#xff0c;以支持企业场景。” 之前鼹鼠哥已经发表了一篇文章&#xff0c;简单介绍了区块链&#xff0c;那么&#xff0c;智能合约又是什么呢&#xff1f;它又是如何助力数据资产变现的呢&#xff1f;…

Python空间分析简明教程

数据世界是一个活生生的、会呼吸的事物。 当一个城市的犯罪率上升时&#xff0c;这是因为现实世界中有人在某个地方犯罪。 有警察局、住宅区和商业区、人口密度以及可以与位置相关联的人的地方。 所有这些东西都存在于数据框和表格之外的世界中。 空间分析使数据科学家能够回答…

成都百洲文化传媒有限公司靠谱吗?怎么样?

随着互联网的迅猛发展&#xff0c;电子商务行业迎来了前所未有的发展机遇。在这个变革的浪潮中&#xff0c;成都百洲文化传媒有限公司凭借其深厚的行业经验和创新的服务模式&#xff0c;正逐渐成为电商服务领域的新领军者。 一、创新引领&#xff0c;塑造电商服务新标准 成都百…

FX110网:Exness平台2024年3月交易量环比增长9%

FX110获知&#xff0c;多资产公司Exness 2024年3月份的客户交易量环比大幅增长9%&#xff0c;达到3.856万亿美元&#xff0c;而上个月为3.534万亿美元。 交易量激增的同时&#xff0c;活跃客户数量不断增加&#xff0c;3月份达到破纪录的836,873位交易者&#xff0c;超过了上个…

51单片机学习笔记——LED点亮

一、独立按键控制LED元器件和原理图 根据厂家给的原理图找到独立按键模块&#xff0c;观察下图我们知道按钮的一个头接GND&#xff0c;一头接IO口。由此可知我们如果需要使用第一个按钮则需要用p31。 二、独立按键控制LED程序 程序编写需要使用到IF else语句 当如果P310时P20…

vue快速入门(十六)事件修饰符

注释很详细&#xff0c;直接上代码 上一篇 新增内容 事件修饰符之阻止冒泡事件修饰符之阻止默认行为 源码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdev…

uniapp开发小程序,点击右上角<重新进入小程序>进行刷新时,设置开屏加载页面

一、需求及问题 问题&#xff1a;使用uniapp开发小程序时&#xff0c;有【学生端】和【企业端】两个入口&#xff0c;一进入小程序默认进入【学生端首页】&#xff0c;但是当前处于【企业端】时&#xff0c;点击右上角<重新进入小程序>进行刷新时&#xff0c;页面默认进…

通过 KEIL 制作 QSPI 接口的外部 Flash 下载算法

1. 引言 随着用户的应用越来越复杂以及 GUI 等需要大存储空间的需求越来越多,很多时候我们需要将代码或数据放在外扩的 Flash 存储空间。但是这样存在一个外部 Flash 烧写的问题,尤其是在应用调试时,需要将代码或数据烧录到外部 Flash。如果调试工具不能够一键烧录,势必会…

ELFK (Filebeat+ELK)日志分析系统

一. 相关介绍 Filebeat&#xff1a;轻量级的开源日志文件数据搜集器。通常在需要采集数据的客户端安装 Filebeat&#xff0c;并指定目录与日志格式&#xff0c;Filebeat 就能快速收集数据&#xff0c;并发送给 logstash 进或是直接发给 Elasticsearch 存储&#xff0c;性能上相…

(vue)el-radio鼠标移入可提示图片

(vue)el-radio鼠标移入可提示图片 效果&#xff1a; <el-form-item label"图表选择"><el-radio-group v-model"formInline.echartType"><el-tooltip v-for"(item, index) of echartTypeOptions" :key"index" placement…

Vue前端框架

1.vue基本使用1 1.vue环境搭建 一般创建vue项目是在cmd命令中用&#xff1a;vue ui 命令&#xff0c;采用ui图形界面的方式直观创建项目。 2.vue基本使用方式&#xff1a;vue组件 3.文本插值 4.属性绑定 5.事件绑定 6.双向绑定 7.条件渲染 2.vue基本使用2 1.axios 安装axios命令…

视频号小店遇到差评怎么办?怎么规避差评问题?有三种解决思路

大家好&#xff0c;我是电商花花。 我们做视频号小店的商家应该都会遇到品退、中差评这些问题&#xff0c;一个差评就可能影响到我们店铺的体验分&#xff0c;尤其是在订单不多的时候&#xff0c;一条差评很有可能让你的店铺的流量、转化率发生骤降&#xff0c;如果体验分太低…

用优先编码器①实现键盘编码电路

描述 请使用优先编码器①实现键盘编码电路&#xff0c;可添加并例化题目中已给出的优先编码器代码。 10个按键分别对应十进制数0-9&#xff0c;按键9的优先级别最高&#xff1b;按键悬空时&#xff0c;按键输出高电平&#xff0c;按键按下时&#xff0c;按键输出低电平&#xf…

计算机网络----第八天

真是交换机怎么操作使用 H3C路由交换产品连接方法&#xff1a; ①SSH |Telnet |console ②直连和间接连接方式 ③上手操作建议&#xff1a; 命令行使用基础&#xff1a; ① system-view #进入系统视图 user-interface vty 0 4 #vty就是用telnet/ssh远程进入交换机的界面(虚…

第十四讲:C语言字符函数和字符串函数

目录 1. 字符分类函数 2、字符转换函数 3. strlen的使⽤和模拟实现 4. strcpy 的使⽤和模拟实现 5. strcat 的使⽤和模拟实现 6. strcmp 的使⽤和模拟实现 7. strncpy 函数的使⽤ 8. strncat 函数的使⽤ 9. strncmp函数的使⽤ 10. strstr 的使⽤和模拟实现 11. strt…

Qwen-WisdomVast (千问-智瀚)

介绍 Qwen-WisdomVast是以Qwen1.5-7B为底座&#xff0c;使用 DORA LORA 的训练方法&#xff0c;在100w高质量中文多轮SFT数据 20w英文多轮SFT数据 2000单轮自我认知数据训练而来的大模型&#xff0c;数学能力相比Qwen1.5-7B-Chat提升了5.16%&#xff0c;在HumanEval数据集上…