设计模式之组合模式(Composite)

news2024/11/26 17:30:02

一、组合模式介绍

       组合模式(Composite Pattern) 的定义是:将对象组合成树形结构以表示整个部分的层

       次结构。组合模式可以让用户统一对待单个对象和对象的组合。

       如在windows操作系统中的目录结构,其实就是树形目录结构,可以通过 tree /f 命令

       将目录实现树形结构展示,如下图所示:

              

       在上图中包含了文件夹和文件两类不同元素,其中在文件夹中可以包含文件,还可以继续

       包含子文件夹。子文件夹中可以放入文件,也可以放入子文件夹。文件夹形成了一种容器

       结构(树形结构),递归结构。如下图所示:

               

       接着我们再来思考虽然文件夹和文件是不同类型的对象,但是他们有一个共性,

       就是都可以被放入文件夹中。 其实文件和文件夹可以被当做是同一种对象看待。

       组合模式其实就是将一组对象(文件夹和文件)组织成树形结构,以表示一种“部分-整体”

       的层次结构,(目录与子目录的嵌套结构)。组合模式让客户端可以统一单个对象(文件)

       和组合对象(文件夹)的处理逻辑(递归遍历)。

       组合模式更像是一种数据结构和算法的抽象,其中数据可以表示成树这种数据结构,

       业务需求可以通过在树上的递归遍历算法来实现。

       我们很容易将“组合模式”和“组合关系”搞混。组合模式最初只是用于解决树形结构的场景,

       更多的是处理对象组织结构之间的问题。而组合关系则是通过将不同对象封装起来完成一

       个统一功能。

二、组合模式原理

        组合模式结构图如下:

                

        组合模式主要包括三种角色,即:

                1)抽象根节点(Component):定义系统各层次对象的共有方法和属性,可以预先

                      定义一些默认行为和属性。

                     在该角色中可以包含所有子类共有行为的声明和实现。在抽象根节点中定义了访问

                     及管理它的子构件的方法,如增加子节点、删除子节点、获取子节点等。

                2)树枝节点(Composite):定义树枝节点的行为,存储子节点,组合树枝节点和

                     叶子节点形成一个树形结构,树枝节点也可以称之为“容器节点或容器对象”。

                     树枝节点可以包含树枝节点,也可以包含叶子节点,它其中有一个集合可以用于存储

                     子节点,实现了在抽象根节点中定义的行为。包括那些访问及管理子构件的方法,在

                      其业务方法中可以递归调用其子节点的业务方法。

                3)叶子节点(Leaf):叶子节点对象,其下再无分支,是系统层次遍历的最小单位。

                      在组合模式中叶子节点没有子节点,它实现了在抽象根节点中定义的行为。

       组合模式用代码表示,代码如下:   

/*******************************************************
 * 抽象根节点
 * 对客户端而言,只需要针对抽象编程,无需关心具体子类实现
 * 抽象根节点可以是抽象类或接口
 *
 * @author lbf
 *******************************************************/
public abstract class Component {

    //增加成员
    public abstract void add(Component c);
    //删除成员
    public abstract void remove(Component c);
    //获取成员
    public abstract Component getChild(int i);
    //业务方法
    public abstract void operation();
}



/*******************************************************
 * 树枝节点
 * 容器对象,即可以包含子节点,也可以包含叶子节点
 *
 * @author lbf
 *******************************************************/
public class Composite extends Component{

    //集合,保存子节点数据
    private ArrayList<Component> list = new ArrayList<>();

    @Override
    public void add(Component c) {
        list.add(c);
    }

    @Override
    public void remove(Component c) {
        list.remove(c);
    }

    @Override
    public Component getChild(int i) {
        return (Component) list.get(i);
    }

    @Override
    public void operation() {
        //在树枝节点中的业务方法,将递归调用其他节点中的operation() 方法
        for (Component component : list) {
            component.operation();
        }
    }
}


/*******************************************************
 * 叶子节点
 * 叶子节点不能包含具体子节点
 * 
 * @author lbf
 *
 *******************************************************/
public class Leaf extends Component{

    @Override
    public void add(Component c) {
        //具体操作
    }

    @Override
    public void remove(Component c) {
        //具体操作
    }

    @Override
    public Component getChild(int i) {
        //具体操作
        return new Leaf();
    }

    @Override
    public void operation() {
        //叶子节点具体业务方法
    }

}

三、组合模式应用示例

       以列出某一目录下所有的文件和文件夹为例来看下组合模式的使用,

       实现类图如下:

               

       示例代码:

/*******************************************************
 * 以列出某一目录下所有的文件和文件夹为例来看下组合模式的使用
 * Entry--抽象类
 *
 * @author lbf
 *******************************************************/
public abstract class Entry {

    //获取文件名
    public abstract String getName();

    //获取文件大小
    public abstract int getSize();

    //添加文件夹或文件
    public abstract Entry add(Entry entry);

    //显示指定目录下的所有信息
    public abstract void printList(String prefix);

    @Override
    public String toString() {
        return getName() + "(" +getSize() + ")";
    }
}


/*******************************************************
 * 文件类
 * 叶子节点
 *
 * @author lbf
 *******************************************************/
public class File extends Entry{

    private String name; //文件名
    private int size; //文件大小

    public File(String name, int size) {
        this.name = name;
        this.size = size;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public int getSize() {
        return size;
    }

    @Override
    public Entry add(Entry entry) {
        return null;
    }

    @Override
    public void printList(String prefix) {

        System.out.println(prefix + "/" + this);
    }
}



/*******************************************************
 * 文件夹类
 * 树枝节点
 *
 * @author lbf
 *******************************************************/
public class Directory extends Entry{

    //文件的名字
    private String name;

    //文件夹与文件的集合
    private ArrayList<Entry> directory = new ArrayList();

    //构造函数
    public Directory(String name) {
        this.name = name;
    }

    //获取文件名称
    @Override
    public String getName() {
        return this.name;
    }

    /**
     * 获取文件大小
     *      1.如果entry对象是File类型,则调用getSize方法获取文件大小
     *      2.如果entry对象是Directory类型,会继续调用子文件夹的getSize方法,形成递归调用.
     */
    @Override
    public int getSize() {
        int size = 0;

        //遍历或者去文件大小
        for (Entry entry : directory) {
            size += entry.getSize();
        }
        return size;
    }

    @Override
    public Entry add(Entry entry) {
        directory.add(entry);
        return this;
    }

    //显示目录
    @Override
    public void printList(String prefix) {
        System.out.println("/" + this);
        for (Entry entry : directory) {
            entry.printList("/" + name);
        }
    }
}

//测试
public class Test {

    public static void main(String[] args) {
        //根节点
        Directory rootDir = new Directory("root");

        //树枝节点
        Directory binDir = new Directory("bin");
        //向bin目录中添加叶子节点
        binDir.add(new File("vi",10000));
        binDir.add(new File("test",20000));

        Directory tmpDir = new Directory("tmp");

        Directory usrDir = new Directory("usr");
        Directory mysqlDir = new Directory("mysql");
        mysqlDir.add(new File("my.cnf",30));
        mysqlDir.add(new File("test.db",25000));
        usrDir.add(mysqlDir);

        rootDir.add(binDir);
        rootDir.add(tmpDir);
        rootDir.add(mysqlDir);

        rootDir.printList("");
    }
}

       

四、组合模式总结

1、组合模式分类

      1)透明组合模式

            透明组合模式中,抽象根节点角色中声明了所有用于管理成员对象的方法,比如在示

            例中 `Component` 声明了 `add`、`remove` 、`getChild` 方法,这样做的好处是确保

            所有的构件类都有相同的接口。透明组合模式也是组合模式的标准形式。

            透明组合模式的缺点是不够安全,因为叶子对象和容器对象在本质上是有区别的,叶

           子对象不可能有下一个层次的对象,即不可能包含成员对象,因此为其提供 add()、

           remove() 等方法是没有意义的,这在编译阶段不会出错,但在运行阶段如果调用这些

           方法可能会出错(如果没有提供相应的错误处理代码)

           透明组合模式结构图如下:

                   

      2)安全组合模式

            在安全组合模式中,在抽象构件角色中没有声明任何用于管理成员对象的方法,而是

            在树枝节点类中声明并实现这些方法。安全组合模式的缺点是不够透明,因为叶子构件

            和容器构件具有不同的方法,且容器构件中那些用于管理成员对象的方法没有在抽象构件

            类中定义,因此客户端不能完全针对抽象编程,必须有区别地对待叶子构件和容器构件。

            安全组合模式结构图如下:

                    

2、组合模式优点

      1)组合模式可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次,它让客户端

            忽略了层次的差异,方便对整个层次结构进行控制。

      2)客户端可以一致地使用一个组合结构或其中单个对象,不必关心处理的是单个对象还是

            整个组合结构,简化了客户端代码。

      3)在组合模式中增加新的树枝节点和叶子节点都很方便,无须对现有类库进行任何修改,

            符合“开闭原则”。

      4)组合模式为树形结构的面向对象实现提供了一种灵活的解决方案,通过叶子节点和树枝

            节点的递归组合,可以形成复杂的树形结构,但对树形结构的控制却非常简单。

3、组合模式缺点

      1)使用组合模式的前提在于,你的业务场景必须能够表示成树形结构。

            所以,组合模式的应用场景也 比较局限,它并不是一种很常用的设计模式。

4、组合模式适用场景

      1)处理一个树形结构,比如,公司人员组织架构、订单信息等;

      2)跨越多个层次结构聚合数据,比如,统计文件夹下文件总数;

      3)统一处理一个结构中的多个对象,比如,遍历文件夹下所有 XML 类型文件内容。

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

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

相关文章

81.【C语言】数据结构之空间复杂度

目录 1.定义 2.例题 计算下列代码中BubbleSort函数的空间复杂度 解: 3.练习 1.求下列代码的空间复杂度 解: 2.求下列代码的空间复杂度 解: 1.定义 对一个算法在运行过程中临时占用存储空间大小的量度,不是程序占用了多少bytes的空间,通常用多少个变量来衡量,也使用大…

公开课学习:软件测试面试3大难题

1.验证码机制的处理&#xff1a;自动化遇到验证码怎么办?怎么测试? 流程&#xff1a;先识别元素&#xff0c;再对元素进行操作。实际上&#xff0c;验证码无法用自动化技术操作解决&#xff0c;都是由开发给万能码&#xff0c;或者屏蔽验证码去解决&#xff01;那如果不能屏…

2.1 HTML5 - Canvas标签

文章目录 引言Canvas标签概述定义实例&#xff1a;创建画布 理解Canvas坐标系概述实例&#xff1a;获取Canvas坐标 获取Canvas环境上下文概述实例&#xff1a;获取Canvas上下文设置渐变色效果 结语 引言 大家好&#xff0c;今天我们要一起探索HTML5中一个非常有趣且强大的特性…

一文通透OpenAI o1:从CoT、Self-Correct/STaR、Self-play RL、MCST等技术细节到工程复现

前言 注意&#xff0c;本文自10.12日起&#xff0c;正在每天更新的过程中.. 可能是去年写或讲的关于ChatGPT原理的文章和课程&#xff0c;影响力太大了 导致​​自从OpenAI o1出来后&#xff0c;每过两天&#xff0c;就有朋友问&#xff0c;“校长 o1啥时候出解读或课程”&…

ESP-01S 通过MQTT控制舵机

一、硬件准备 1. ESP-01S 模块 负责控制和联网的单片机为 ESP-01S,其核心是乐鑫科技(ESPRESSIF)设计的 ESP8266,也是大部分嵌入式开发发烧友常用的 WiFi 模块。 外观和引脚分布如下图: 具体引脚定义如下表: 脚序 名称 功能说明 1 GND 接地 2 IO2 GPIO…

IP地址如何支持远程办公?

由于当今社会经济的飞速发展&#xff0c;各个方向的业务都不免接触到跨省、跨市以及跨国办公的需要&#xff0c;随之而来的远程操作的不方便&#xff0c;加载缓慢&#xff0c;传输文件时间过长等困难&#xff0c;如何在万里之外实现远程办公呢&#xff1f;我们以以下几点进行阐…

【NLP自然语言处理】探索注意力机制:解锁深度学习的语言理解新篇章

目录 &#x1f354; 注意力机制介绍 1.1 注意力概念 1.2 注意力计算规则 1.3 常见的注意力计算规则 &#x1f354; 什么是注意力机制 &#x1f354; 注意力机制的作用 &#x1f354; 注意力机制实现步骤 4.1 步骤 4.2 代码实现 &#x1f354; 小结 学习目标 &#x1…

C++求日期差值题目

C日期差值题目&#xff08;牛客网&#xff09;题目超链接 仅个人思路不是最优解 仔细阅读地题目&#xff0c;要求输入连续的两串数字表示两个日期 所以我感觉日期类不太方便&#xff08;也许是我实力不允许&#xff09; cin使用起来就不太方便&#xff0c;我这里选择使用sca…

ubuntu服务器监控程序崩溃自动重启

环境&#xff1a;监控程序运行情况分为两种情况&#xff0c;一种带界面&#xff0c;一种控制台程序&#xff0c;带界面程序采用脚本监控方式&#xff0c;不带界面采用Supervisor工具监控。 1. 自动重启带界面程序&#xff1a; #!/bin/sh while true; do processExistps aux | …

Android 未来可能支持 Linux 应用,Linux 终端可能登陆 Android 平台

近日&#xff0c;根据 android authority 的消息&#xff0c;Google 正在开发适用于 Android 的 Linux 终端应用&#xff0c;而终端应用可以通过开发人员选项启用&#xff0c;并将 Debian 安装在虚拟机中。 在几周前&#xff0c;Google 的工程师开始为 Android 开发新的 Termi…

2021年新版Go工程师大厂面试题Go面试题容器技术

Go面试题--容器技术 1、为什么需要 DevOps 在当今&#xff0c;软件开发公司在软件新版本发布方面&#xff0c;多尝试通过发布一系列以小的 特性改变集为目标的新软件版本&#xff0c;代替发布一个大特性改变集的新软件版本的 方式。这种方式有许多优点&#xff0c;诸如&#x…

分布式数据库的进度管理:TiDB 备份恢复工具 PiTR 的原理与实践

导读 对于一款企业级数据库产品而言&#xff0c;数据的安全性和可恢复性是至关重要的。PiTR&#xff08;Point in Time Restore&#xff09;作为 TiDB 备份工具的核心功能之一&#xff0c;提供了一种精细的数据恢复能力&#xff0c;允许用户将数据库集群恢复到过去的任意时间点…

通过多元蒙特卡罗模拟来预测股票价格的日内波动性

作者&#xff1a;老余捞鱼 原创不易&#xff0c;转载请标明出处及原作者。 写在前面的话&#xff1a; 日内价格波动对交易策略的重要性不言而喻&#xff0c;尤其是美跨式交易策略&#xff08;The American straddle&#xff09;。由于无法预测所有影响股价的因素&#x…

【原创】java+springboot+mysql法律咨询网系统设计与实现

个人主页&#xff1a;程序猿小小杨 个人简介&#xff1a;从事开发多年&#xff0c;Java、Php、Python、前端开发均有涉猎 博客内容&#xff1a;Java项目实战、项目演示、技术分享 文末有作者名片&#xff0c;希望和大家一起共同进步&#xff0c;你只管努力&#xff0c;剩下的交…

loadlocale.c:130: _nl_intern_locale_data: failed已放弃 (核心已转储)]问题

在进行交叉编译cortex-a9架构时&#xff0c;出现以上错误。 问题描述&#xff1a; 在使用 arm-none-linux-gnueabi-gdb 进行 Cortex-A9 架构交叉编译调试时&#xff0c;出现如下错误&#xff1a; arm-none-linux-gnueabi-gdb: loadlocale.c:130: _nl_intern_locale_data: As…

新型物联网电力数据采集器 智能网关通讯协议有哪些?

随着智能化技术的快速发展&#xff0c;电气监测与管理在各个域的应用愈发重要&#xff0c;在物联网&#xff08;IoT&#xff09;应用的发展中&#xff0c;网关扮演着至关重要的角色。它作为连接设备与云平台或数据中心的桥梁&#xff0c;负责数据的收集、处理和传输。网关不仅支…

鸿蒙开发之ArkUI 界面篇 三十四 容器组件Tabs二 常用属性

barPosition&#xff1a;位置开头或结尾,vertical 水平或者垂直,scrollable手势滑动切换,animationDuration 滑动动画时间。BarPosition.Start 效果如下图&#xff1a; BarPosition.End 效果如下图&#xff1a; 如果显示在左边&#xff0c;使用的是vertical属性,下图&#xff0…

Chrome(谷歌)浏览器 数据JSON格式美化 2024显示插件安装和使用

文章目录 目录 文章目录 安装流程 小结 概要安装流程技术细节小结 概要 没有美化的格式浏览器展示 美化之后效果图 安装流程 下载地址 https://github.com/gildas-lormeau/JSONVue 点击下载 下载成功&#xff0c;如图所示 解压文件 添加成功&#xff0c;如图所示 通过浏览器…

密码学算法概览大全

区块链密码学 目录 对称密码算法 1. 流密码2. 分组密码3. 对称密码算法小结4. 对称密码算法在区块链中的应用 非对称密码算法 1. RSA2. ECC3. 非对称密码算法小结4. 非对称密码算法在区块链中的应用 Hash函数 1. SHA2. RipeMD-1603. Hash函数在区块链中的应用 PKI 1. PKI组成2…

STM32——USART原理及应用

1.什么是USART&#xff1f; 1.1 基本概念 USART英文全称&#xff1a;universal asynchronous receiver and transmitter &#xff0c;翻译过来就是&#xff1a;通用同步异步收/发器。USART是STM32内部集成的硬件外设&#xff0c;可根据数据寄存器的一个字节数据自动生成数据帧…