【设计模式深度剖析】【4】【结构型】【组合模式】| 以文件系统为例加深理解

👈️上一篇:适配器模式    |   下一篇:桥接模式👉️

设计模式-专栏👈️

目 录

  • 组合模式
  • 定义
    • 英文原话
    • 直译
    • 如何理解?
  • 3个角色
    • UML类图
    • 代码示例
  • 组合模式的优点
  • 组合模式的使用场景
  • 示例解析:文件系统

组合模式

组合模式(Composite Pattern)也叫合成模式,用来描述部分与整体的关系。

  • 高层模块调用简单。一棵树形结构中的所有节点都是 Component,局部和整体对调用者来说没有任何区别,即高层模块不必关心自己处理的是单个对象还是整个组合结构,简化了高层模块的代码。
  • 节点自由增加。使用组合模式后,如果想增加一个树枝节点、树叶节点只需要找到其父节点即可。

当我们发现需求中是体现部分与整体层次的结构时,以及希望用户可以忽略组合对象与单个对象的不同,统一地使用组合结构中的所有对象时,就应该考虑用组合模式了。

定义

英文原话

Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly.

直译

将对象组合成树形结构来表示“部分-整体”的层次结构。组合模式使得客户端能够统一地处理单个对象和对象的组合。

如何理解?

叶子构件和容器构件实现了抽象构件,即实现了相同的接口,用户对单个对象和组合对象的使用具有一致性。

3个角色

UML类图

CompositePattern.png

组合模式(Composite Pattern)允许我们将对象组合成树形结构以表示“部分整体”的层次结构,使得客户端以统一的方式处理单个对象和对象的组合。以下是组合模式中的主要角色:

  1. 抽象构件(Component):它可以是接口或抽象类,为叶子和容器对象声明接口,在该接口中包含用于管理子对象的方法以及用于自身操作的方法。在组合模式中,抽象构件定义了叶子和容器构件的共同行为。
  2. 叶子构件(Leaf):叶子对象继承自抽象构件,它没有子节点,通常用于实现抽象构件中的业务方法。在组合结构中,叶子节点没有子节点,其实现了在抽象构件中定义的行为。
  3. 容器构件(Composite):容器对象也继承自抽象构件,并包含一组子构件。它实现了在抽象构件中定义的行为,并提供了添加、删除和访问其子对象的方法。容器构件可以包含其他容器或叶子,从而实现复杂的树形结构。
  4. 客户端(Client):通过抽象构件接口与组合结构进行交互。对于客户端而言,叶子对象和容器对象是一致的,客户端不需要区分它们。

代码示例

以下是一个简单的Java示例来说明组合模式:

// 抽象构件  
public interface Component {
    void operation();
}
// 叶子构件  
public class Leaf implements Component{
    @Override
    public void operation() {
        // 业务处理逻辑
        System.out.println("leaf...");
    }
}
// 容器构件  
public class Composite implements Component {
    // 构件容器
    private ArrayList<Component> componentList = new ArrayList<>();

    // 添加构件
    public void add(Component component) {
        this.componentList.add(component);
    }

    // 删除构件
    public void remove(Component component) {
        this.componentList.remove(component);
    }

    // 获取子构件
    public ArrayList<Component> getChild() {
        return this.componentList;
    }

    @Override
    public void operation() {
        // 业务逻辑
        System.out.println("branch...");
    }
}
// 客户端代码  
public class DemoTest {
    public static void main(String[] args) {
        // 创建一个根节点
        Composite root = new Composite();
        root.operation();
        // 创建树枝节点
        Composite branch = new Composite();
        // 创建叶子节点
        Leaf leaf = new Leaf();
        // 构建树形结构
        root.add(branch);
        branch.add(leaf);
        display(root);
    }

    // 遍历树(递归)
    public static void display(Composite root) {
        for (Component c : root.getChild()) {
            if(c instanceof Leaf){// 如果节点类型是叶子节点
                c.operation();
            }else{// 树枝节点
                c.operation();
                display((Composite) c);
            }
        }
    }
}
/* Output:
branch...
branch...
leaf...
*///~

在这个例子中,我们有一个Component接口,它定义了一个名为operation的方法。Leaf类实现了这个接口,并提供了具体的实现。Composite类同样实现了Component接口,并维护了一个子组件的列表。

组合模式的优点

  1. 高层模块调用简单。一棵树形机构中的所有节点都是 Component,局部和整体对调用者来说没有任何区别即高层模块不必关心自己处理的是单个对象还是整个组合结构,简化了高层模块的代码
  2. 节点自由增加。使用组合模式后,如果想增加一个树枝节点、树叶节点只需要找到其父节点即可。

组合模式的使用场景

使用组合模式的典型场景如下。

  1. 需要描述对象的部分和整体的等级结构,如树形菜单、文件和文件夹管理
  2. 需要客户端忽略个体构件和组合构件的区别,平等对待所有的构件

示例解析:文件系统

FileSystemDemo.png

在生活中,一个常见的组合模式的例子是文件系统。文件系统中的文件和文件夹可以看作是组合模式的实现,其中文件夹可以包含文件和其他文件夹(子文件夹),而文件则不包含任何子项。

以下是使用Java实现的示例,模拟了一个简单的文件系统:

// 抽象构件:文件或文件夹  
public interface FileSystemElement {  
    void display();  
}  
// 叶子构件:文件  
public class File implements FileSystemElement {  
    private String name;  
  
    public File(String name) {  
        this.name = name;  
    }  
  
    @Override  
    public void display() {  
        System.out.println("File: " + name);  
    }  
}  
// 容器构件:文件夹  
public class Folder implements FileSystemElement {  
    private String name;  
    private List<FileSystemElement> children = new ArrayList<>();  
  
    public Folder(String name) {  
        this.name = name;  
    }  
  
    public void add(FileSystemElement element) {  
        children.add(element);  
    }  
  
    public void remove(FileSystemElement element) {  
        children.remove(element);  
    }  
  
    @Override  
    public void display() {  
        System.out.println("Folder: " + name);  
        for (FileSystemElement child : children) {  
            child.display();  
        }  
    }  
}  
// 客户端代码
public class DemoTest {
    public static void main(String[] args) {
        // 创建文件夹和文件  
        Folder rootFolder = new Folder("root");
        Folder documentsFolder = new Folder("Documents");
        Folder picturesFolder = new Folder("Pictures");
        File file1 = new File("example.txt");
        File file2 = new File("image.jpg");

        // 将文件和文件夹添加到对应的父文件夹中  
        rootFolder.add(documentsFolder);
        rootFolder.add(picturesFolder);
        documentsFolder.add(file1);
        picturesFolder.add(file2);

        // 显示整个文件系统的结构  
        rootFolder.display();
    }
}

/* Output:
Folder: root
Folder: Documents
File: example.txt
Folder: Pictures
File: image.jpg
*///~

在这个示例中,FileSystemElement是抽象构件接口,它声明了一个display方法用于显示文件或文件夹的信息。File类实现了这个接口,表示一个具体的文件。Folder类也实现了这个接口,表示一个文件夹,并且它有一个children列表来存储其子元素(文件和文件夹)。Folder类还提供了添加和删除子元素的方法。

DemoTest类的main方法中,我们创建了一个根文件夹rootFolder,并添加了documentsFolderpicturesFolder两个子文件夹。接着,我们向这两个文件夹中分别添加了一个文件和图片。最后,我们调用rootFolderdisplay方法来显示整个文件系统的结构。

这个输出展示了组合模式中的层次结构,其中文件夹可以包含文件和子文件夹,而文件则不包含任何子项。

👈️上一篇:适配器模式    |   下一篇:桥接模式👉️

设计模式-专栏👈️

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

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

相关文章

C#子窗体嵌入主窗体

上位机开发中&#xff0c;经常会需要将子窗体嵌入到主窗体。 运行结果 核心实现&#xff1a; private void button2_Click(object sender, EventArgs e){Form3 childForm new Form3();//判断容器中是否已经打开子窗体&#xff0c;如果打开现将其关闭foreach (Control item in…

磐启PAN2013 2.4GHz无线收发SOC

PAN2013是一款集成了8位MCU和2568bits EEPROM的无线收发SoC芯片。该芯片工作2.400~2.483GHz世界通用ISM频段&#xff0c;且集成射频收发机、频率发生器、晶体振荡器、调制解调器和低功耗MCU等功能模块&#xff0c;并且支持一对多组网和带ACK的通信模式。 用户通过MCU的I/O口向…

虚拟化平台之Proxmox VE 安装

介绍 Proxmox VE是一种基于Debian Linux和KVM的虚拟化平台&#xff0c;也可称之为Proxmox Virtual Environment。 Proxmox VE具有非常友好的用户界面&#xff0c;基于JAVA的UI和内核接口&#xff0c;方便用户登录到VM客户进行操作&#xff0c;还具有易用的模板功能&#xff0…

如果创办Google

本文是一篇演讲稿&#xff0c;来自于《黑客与画家》一书的作者保罗*格雷厄姆&#xff0c;被称为硅谷创业之父。这是他为14至15岁的孩子们做的一次演讲&#xff0c;内容是关于如果他们将来想创立一家创业公司&#xff0c;现在应该做些什么。很多学校认为应该向学生们传授一些有关…

【Leetcode 160】环形链表——双指针,细节讲解

题目 给你一个链表的头节点 head &#xff0c;判断链表中是否有环。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置&#…

腾讯云COS上传文件出现的问题

1、没有配置 ObjectMetadata 的文件长度 腾讯云COS上传文件出现数据损坏问题_no content length specified for stream data. strea-CSDN博客 2、 使用 FileInputStream使用完没有及时关闭导致报错 ClientAbortException: java.nio.channels.ClosedChannelException 添加…

【Qt Creator】跨平台的C++图形用户界面应用程序开发框架---QT

&#x1f341;你好&#xff0c;我是 RO-BERRY &#x1f4d7; 致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 &#x1f384;感谢你的陪伴与支持 &#xff0c;故事既有了开头&#xff0c;就要画上一个完美的句号&#xff0c;让我们一起加油 目录 1.互联网的核心岗位以及职…

高斯过程学习笔记

目录 基础知识 例子 推荐 A Visual Exploration of Gaussian Processes (distill.pub) AB - Introduction to Gaussian Processes - Part I (bridg.land) 基础知识 高斯过程回归&#xff08;Gaussian Process Regression&#xff09; - 知乎 (zhihu.com) 高斯过程&#x…

【Mac】 CleanMyMac X for mac V4.15.2中文修复版安装教程

软件介绍 CleanMyMac X是一款为Mac设计的优秀软件&#xff0c;旨在帮助用户优化其设备的性能并提供清理和维护功能。以下是 CleanMyMac X的一些主要功能和特点&#xff1a; 1.系统性能优化&#xff1a;软件可以扫描和修复潜在的性能问题&#xff0c;包括无效的登录项、大文件…

【Web】CISCN 2024初赛 题解(全)

目录 Simple_php easycms easycms_revenge ezjava mossfern sanic Simple_php 用php -r进行php代码执行 因为ban了引号&#xff0c;考虑hex2bin&#xff0c;将数字转为字符串 php -r eval(hex2bin(16进制)); 注意下面这段报错&#xff0c;因为加不了引号&#xff0c;开…

[集群聊天服务器]----(十一) 使用Redis实现发布订阅功能

接着上文&#xff0c;[集群聊天服务器]----(十)Nginx的tcp负载均衡配置–附带截图&#xff0c;我们配置nginx&#xff0c;使用了多台服务端来提高单机的并发量&#xff0c;接下来我们回到项目中&#xff0c;思考一下&#xff0c;各个服务端之间怎么进行通信呢&#xff1f; 配置…

滑动窗口-java

主要通过单调队列来解决滑动窗口问题&#xff0c;得到滑动窗口中元素的最大值和最小值。 目录 前言 一、滑动窗口 二、算法思路 1.滑动窗口 2.算法思路 3.代码详解 三、代码如下 1.代码如下 2.读入数据 3.代码运行结果 总结 前言 主要通过单调队列来解决滑动窗口问题&#xff…

文件上传漏洞:pikachu靶场中的文件上传漏洞通关

目录 1、文件上传漏洞介绍 2、pikachu-client check 3、pikachu-MIME type 4、pikachu-getimagesize 最近在学习文件上传漏洞&#xff0c;这里使用pikachu靶场来对文件上传漏洞进行一个复习练习 废话不多说&#xff0c;开整 1、文件上传漏洞介绍 pikachu靶场是这样介绍文…

Docker快速安装SQL Server 2022

说明&#xff1a; 系统&#xff1a;Ubuntu 24.04 LTS 拉取SQL Server Docker镜像 docker pull mcr.microsoft.com/mssql/server:2022-CU12-ubuntu-22.04创建数据目录 sudo mkdir /var/mssql_data sudo chmod 777 /var/mssql_data说明&#xff1a; 权限设置为777&#xff0…

[集群聊天服务器]----(十)Nginx的tcp负载均衡配置--附带截图

接着上文&#xff0c;我们剖析了服务端和客户端的代码&#xff0c;但是单台服务器的并发量是有限的&#xff0c;面对并发量的要求&#xff0c;我们就需要引入Nginx来实现并发量的要求&#xff0c;将用户请求分发到不同的服务器上分担压力&#xff0c;这就是负载均衡。 选择负…

最新php项目加密源码

压缩包里有多少个php就会被加密多少个PHP、php无需安装任何插件。源码全开源 如果上传的压缩包里有子文件夹&#xff08;子文件夹里的php文件也会被加密&#xff09;&#xff0c;加密后的压缩包需要先修复一下&#xff0c;步骤&#xff1a;打开压缩包 》 工具 》 修复压缩文件…

JavaSE——集合框架二(2/6)-综合案例-斗地主游戏(做牌、洗牌、发牌、排序、看牌)

目录 需求与分析 具体实现 牌类定义 房间类定义 初步测试 启动游戏 运行案例 需求与分析 需求 总共有54张牌点数&#xff1a;"3","4","5","6","7","8","9","10","J",&qu…

MyBatis的基础操作

目录 一.什么是MyBatis? 二.使用MyBatis的准备工作 1.引入依赖: 2.配置数据库连接字符串(建立MaBatis和MySQL的连接) 3.在model包中建立数据库对应的实体类UserInfo 三.通过注解的方式实现MyBatis的开发 1.插入语句(Insert) 2.删除语句(Delete) 3.更新语句(Update) 4…

驱动开发:内核MDL读写进程内存

100编程书屋_孔夫子旧书网 MDL内存读写是最常用的一种读写模式,通常需要附加到指定进程空间内然后调用内存拷贝得到对端内存中的数据,在调用结束后再将其空间释放掉,通过这种方式实现内存读写操作,此种模式的读写操作也是最推荐使用的相比于CR3切换来说,此方式更稳定并不会…

c语言--结构体

前言 欢迎来到我的博客 个人主页:北岭敲键盘的荒漠猫-CSDN博客 结构体概念简介 c语言数组是一些相同类型的数据的集合。 这个结构体就是一些可以是不同类型的集合。 比如描述班里的一个人&#xff0c;他可能需要名字(字符串),也需要年龄(整数)。 这种情况就需要用结构体。 …