当前位置: 首页 > news >正文

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

我们很容易将“组合模式”和“组合关系”搞混。组合模式最初只是用于解决树形结构的场景,更多的是处理对象组织结构之间的问题。而组合关系则是通过将不同对象封装起来完成一个统一功能.

 

组合模式介绍

组合模式(Composite Pattern) 的定义是:将对象组合成树形结构以表示整个部分的层次结构.组合模式可以让用户统一对待单个对象和对象的组合.

比如: windows操作系统中的目录结构,其实就是树形目录结构,通过tree命令实现树形结构展示.

在上图中包含了文件夹和文件两类不同元素,其中在文件夹中可以包含文件,还可以继续包含子文件夹.子文件夹中可以放入文件,也可以放入子文件夹. 文件夹形成了一种容器结构(树形结构),递归结构.

接着我们再来思考虽然文件夹和文件是不同类型的对象,但是他们有一个共性,就是 都可以被放入文件夹中. 其实文件和文件夹可以被当做是同一种对象看待.

组合模式其实就是将一组对象(文件夹和文件)组织成树形结构,以表示一种'部分-整体' 的层次结构,(目录与子目录的嵌套结构). 组合模式让客户端可以统一单个对象(文件)和组合对象(文件夹)的处理逻辑(递归遍历).

组合模式更像是一种数据结构和算法的抽象,其中数据可以表示成树这种数据结构,业务需求可以通过在树上的递归遍历算法来实现.

组合模式原理

组合模式主要包含三种角色:

  • 抽象根节点(Component):定义系统各层次对象的共有方法和属性,可以预先定义一些默认行为和属性。

    在该角色中可以包含所有子类共有行为的声明和实现.在抽象根节点中定义了访问及管理它的子构建的方法,如增加子节点、删除子节点、获取子节点等.

  • 树枝节点(Composite):定义树枝节点的行为,存储子节点,组合树枝节点和叶子节点形成一个树形结构。

    树枝节点可以包含树枝节点,也可以包含叶子节点,它其中有一个集合可以用于存储子节点,实现了在抽象根节点中定义的行为.包括那些访问及管理子构建的方法,在其业务方法中可以递归调用其子节点的业务方法.

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

    在组合结构中叶子节点没有子节点,它实现了在抽象根节点中定义的行为.

组合模式实现

组合模式的关键在于定义一个抽象根节点类,它既可以代表叶子,又可以代表树枝节点,客户端就是针对该抽象类进行编程,不需要知道它到底表示的是叶子还是容器,可以对其进行统一处理.

树枝节点对象和抽象根节点类之间建立了一个聚合关联关系,在树枝节点对象中既可以包含叶子节点,还可以继续包含树枝节点,以此实现递归组合,形成一个树形结构.

代码实现

/*** 抽象根节点*      对于客户端而言将针对抽象编程,无需关心其具体子类是容器构建还是叶子构建.**/
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(); //业务方法}/*** 叶子节点*      叶子节点中不能包含子节点**/
public class Leaf extends Component {@Overridepublic void add(Component c) {//具体操作}@Overridepublic void remove(Component c) {//具体操作}@Overridepublic Component getChild(int i) {//具体操作return new Leaf();}@Overridepublic void operation() {//叶子节点具体业务方法}
}/*** 树枝节点*      容器对象,可以包含子节点**/
public class Composite extends Component {private ArrayList<Component> list = new ArrayList<>();@Overridepublic void add(Component c) {list.add(c);}@Overridepublic void remove(Component c) {list.remove(c);}@Overridepublic Component getChild(int i) {return (Component) list.get(i);}@Overridepublic void operation() {//在树枝节点中的业务方法,将递归调用其他节点中的operation() 方法for (Component component : list) {component.operation();}}
}

组合模式应用实例

下面我们通过一段程序来演示一下组合模式的使用. 程序的功能是列出某一目录下所有的文件和文件夹.类图如下:

 我们按照下图的表示,进行文件和文件夹的构建.

 Entry类: 抽象类,用来定义File类和Directory类的共性内容

/*** Entry抽象类,表示目录条目(文件+文件夹)的抽象类**/
public abstract class Entry {public abstract String getName(); //获取文件名public abstract int getSize(); //获取文件大小//添加文件夹或文件public abstract Entry add(Entry entry);//显示指定目录下的所有信息public abstract void printList(String prefix);@Overridepublic String toString() {return getName() + "(" +getSize() + ")";}
}

File类,叶子节点,表示文件.

/*** File类 表示文件**/
public class File extends Entry {private String name; //文件名private int size; //文件大小public File(String name, int size) {this.name = name;this.size = size;}@Overridepublic String getName() {return name;}@Overridepublic int getSize() {return size;}@Overridepublic Entry add(Entry entry) {return null;}@Overridepublic void printList(String prefix) {System.out.println(prefix + "/" + this);}}

Directory类,树枝节点,表示文件

/*** Directory表示文件夹**/
public class Directory extends Entry{//文件的名字private String name;//文件夹与文件的集合private ArrayList<Entry> directory = new ArrayList();//构造函数public Directory(String name) {this.name = name;}//获取文件名称@Overridepublic String getName() {return this.name;}/*** 获取文件大小*      1.如果entry对象是File类型,则调用getSize方法获取文件大小*      2.如果entry对象是Directory类型,会继续调用子文件夹的getSize方法,形成递归调用.*/@Overridepublic int getSize() {int size = 0;//遍历或者去文件大小for (Entry entry : directory) {size += entry.getSize();}return size;}@Overridepublic Entry add(Entry entry) {directory.add(entry);return this;}//显示目录@Overridepublic void printList(String prefix) {System.out.println("/" + this);for (Entry entry : directory) {entry.printList("/" + name);}}
}

测试

public class Client {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 ) 组合模式的分类

  • 透明组合模式

    透明组合模式中,抽象根节点角色中声明了所有用于管理成员对象的方法,比如在示例中 Component 声明了 addremovegetChild 方法,这样做的好处是确保所有的构建类都有相同的接口。透明组合模式也是组合模式的标准形式。

    透明组合模式的缺点是不够安全,因为叶子对象和容器对象在本质上是有区别的,叶子对象不可能有下一个层次的对象,即不可能包含成员对象,因此为其提供 add()、remove() 等方法是没有意义的,这在编译阶段不会出错,但在运行阶段如果调用这些方法可能会出错(如果没有提供相应的错误处理代码)

     

  • 在安全组合模式中,在抽象构建角色中没有声明任何用于管理成员对象的方法,而是在树枝节点类中声明并实现这些方法。安全组合模式的缺点是不够透明,因为叶子构建和容器构建具有不同的方法,且容器构建中那些用于管理成员对象的方法没有在抽象构建类中定义,因此客户端不能完全针对抽象编程,必须有区别地对待叶子构建和容器构建。

2 ) 组合模式优点

  • 组合模式可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次,它让客户端忽略了层次的差异,方便对整个层次结构进行控制。

  • 客户端可以一致地使用一个组合结构或其中单个对象,不必关心处理的是单个对象还是整个组合结构,简化了客户端代码。

  • 在组合模式中增加新的树枝节点和叶子节点都很方便,无须对现有类库进行任何修改,符合“开闭原则”。

  • 组合模式为树形结构的面向对象实现提供了一种灵活的解决方案,通过叶子节点和树枝节点的递归组合,可以形成复杂的树形结构,但对树形结构的控制却非常简单。

3 ) 组合模式的缺点

  • 使用组合模式的前提在于,你的业务场景必须能够表示成树形结构。所以,组合模式的应用场景也 比较局限,它并不是一种很常用的设计模式。

4 ) 组合模式使用场景分析

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

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

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

相关文章:

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

我们很容易将“组合模式”和“组合关系”搞混。组合模式最初只是用于解决树形结构的场景&#xff0c;更多的是处理对象组织结构之间的问题。而组合关系则是通过将不同对象封装起来完成一个统一功能. 组合模式介绍 组合模式(Composite Pattern) 的定义是&#xff1a;将对象组合…...

VScode开发工具总结

目录 高效使用VScode工具 vscode 字体放大缩小快捷键 Vscode翻译插件推荐 code-translator插件​编辑 VsCode自动格式化代码 高效使用VScode工具...

opencv 解码视频流,c++ 代码写法

#include <opencv2/opencv.hpp> #include <iostream> using namespace cv; using namespace std; int main() { // 打开视频流 VideoCapture cap("your_video_stream_url"); // 检查视频流是否成功打开 if (!cap.isOpened()) { …...

Android 12.0 修改wifi信号强度

1.前言 在12.0的系统rom产品定制化开发中,在进行产品开发中,对应系统定制会有各种各样的需求,对纯wifi产品而言,对于wifi要求也是越来越高,因此有客户要求对wifi信号强度做定制,修改信号强度来增强显示wifi信号,所以要对wifi显示信号强度的相关代码做修改 2.修改wifi…...

Linux——容器简介

1.容器技术 软件应用通常依赖运行时环境提供的其他库、配置文件或服务&#xff0c;在运行时环境安装在物理主机或虚拟机上运行的操作系统&#xff0c;同时应用依赖项也会随着该操作系统一起安装在主机上。 主要弊端是依赖项会受运行时环境的影响&#xff0c;引用所需的支持软件…...

CMOS图像传感器——pipeline像素控制

一、传统像素操作 传统CMOS图像传感器的芯片架构中,像素的控制信号从水平方向驱动,像素的源极跟随器输出电压垂直地输出到位于顶部和底部的模拟前端读出电路,其具体实现方式如下图所示,其中RST, TX和SEL是像素水平控制信号,像素输出电压PIX OUT垂直地传输到读出电路。 …...

AI工具(ChatGPT)常用指令,持续更新...

要国内使用AI工具&#xff0c;关注&#xff1a;码视野&#xff0c;回复&#xff1a;1002使用VensiGPT时&#xff0c;当你给的指令越精准&#xff0c;它回复就会越完美&#xff0c;例如&#xff0c;如果你要请它协助撰写文案&#xff0c;如果没有给与指定情景与目标&#xff0c;…...

36--Django-项目实战-全栈开发-基于django+drf+vue+elementUI企业级项目开发流程-前台项目准备

前台项目创建 1.命令行创建vue项目(参考使用Vue脚手架快速搭建项目) vue create 项目名2.安装插件vue.js 3.配置全局css,在assets/css/global.css /* 声明全局样式和项目的初始化样式 */ body, h1, h2, h3, h4, h5, h6, p, table, tr, td, ul, li, a, form, input, selec…...

游戏算法-游戏AI行为树,python实现

参考文章&#xff1a;Behavior trees for AI: How they work (gamedeveloper.com) 本文主要参考上述weizProject Zomboid 的开发者 Chris Simpson文章的概念&#xff0c;用伪代码实现代码例子 AI概述 游戏AI是对游戏内所有非玩家控制角色的行为进行研究和设计&#xff0c;使得游…...

【新2023Q2模拟题JAVA】华为OD机试 - 矩阵最值 or 计算二维矩阵的最大值

最近更新的博客 华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为od机试,独家整理 已参加机试人员的实战技巧本篇题解:矩阵最值 or 计算二维矩阵的最…...

递归过程与递归工作栈

首先了解一下任意两个函数之间进行调用的情况: 比如在a函数里面调用b函数&#xff0c;那么在使用b函数之前&#xff0c;计算机还会1.把实参&#xff0c;返回地址给复制下来&#xff08;但只是复制而已&#xff0c;地址不是原来的。&#xff09;如图&#xff1a; 此外2.为被调用…...

B 树的简单认识

理解 B 树的概念 B 树是一种自平衡的查找树&#xff0c;能够保持数据有序。这种数据结构能够让查找数据、顺序访问、插入数据及删除数据的动作&#xff0c;都能在对数时间内完成。 同一般的二叉查找树不同&#xff0c;B 树是一棵多路平衡查找树&#xff0c;其特性是&#xff…...

【大数据Hive3.x数仓开发】窗口函数案例:连续N次登录的用户;级联累加求和;分组TopN

文章目录1 统计连续N次登录的用户&#xff08;N>2&#xff09;自连接过滤实现窗口函数lead()实现2 级联累加求和自连接窗口函数sum()实现3 分组TopN问题对窗口函数的讲解part见&#xff1a;【大数据Hive3.x数仓开发】函数–窗口函数 1 统计连续N次登录的用户&#xff08;N&…...

openpyxl库自动填充excel实例分享

openpyxl可以通过编写Python脚本实现自动化Excel操作&#xff0c;包括自动填充数据、格式化单元格、生成图表等操作。 以下是一个常见的自动化Excel操作示例&#xff1a; 自动填充数据&#xff1a; from openpyxl import Workbook from openpyxl.utils import get_column_l…...

ICLR2021清华团队做的知识蒸馏提升detector的点的工作paper 小陈读论文系列

这个作者栏目就是一个词 清爽 牛逼不需要花里胡哨哈哈 无疑是有点tian了哈哈 不重要 毕竟有机会研读 梦中情笑的paper 还是很感激的 真的 很清爽啊 很多KD的工作确实 在下游任务呢效果不是很好 然后就引出了自己的关于提升知识蒸馏在OD方面的工作 OD 首先就有两个问题 1.前…...

Java核心技术知识点笔记—集合框架

前言&#xff1a;Java最初版本只为最常用的数据结构提供了很少的一组类&#xff1a;Vector、Stack、Hashtable、BitSet和Enumeration接口。其中&#xff0c;Enumeration接口提供了一种用于访问任意容器中各个元素的抽象机制。与现代数据结构类库常见情况一样&#xff0c;Java集…...

Rsync数据同步工具

一、什么是Rsync Rsync是一款开源的&#xff0c;快速的&#xff0c;多功能的&#xff0c;可实现全量及增量&#xff08;差异化备份&#xff09;的本地或远程数据同步备份的优秀工具。 Rsync软件适用于Unix、Linux、Windows等多种操作系统。 &#xff08;1&#xff09;可使本地…...

redux小结

store.dispatch(action对象) 在 dispatch 中调用 action 方法返回 action 对象 // /actions/index.js /*** Action&#xff1a;* action本质上是一个 JS 对象&#xff1b;* 必须要包含 type 属性&#xff0c;否则会报错&#xff1b;* 只描述了有事情要发生&#xff0c…...

【Python】【进阶篇】十、Pygame的Font文本和字体

目录十、Pygame的Font文本和字体10.1 font.SysFont()10.2 font.Font()10.3 字体对象方法十、Pygame的Font文本和字体 Pygame 通过pygame.font模块来创建一个字体对象&#xff0c;从而实现绘制文本的目的。 该模块的常用方法如下所示&#xff1a; 名称说明pygame.font.init()初…...

【从零开始学习 UVM】10.8、UVM TLM —— UVM TLM Example

文章目录 subComp1subComp2ComponentAsubComp3ComponentBTop Env/Test这个 UVM TLM 示例使用之前文章中讨论的 put 端口、TLM FIFO 和 get 端口来构建一个具有不同层次的 TLM 端口的测试台。 下面定义了一个名为Packet的类,作为从一个组件传输到另一个组件的数据项。这个类对象…...

获取自己所上传资源的下载量

import requestsurl = https://download-console-api.csdn.net/v1/user/sources/getUploadListByUserName?status=2&pageNum=1&pageSize=100 cookie = # 这里填自己的cookie header = {"authority": "download-console-api.csdn.net","met…...

Aspose.cells模板导出使用记录

简述 用Aspose.cells导出可以方便地将数据到Excel文档中&#xff0c;简单的直接将DataTable列表写入即可&#xff0c;复杂的格式一般会先做好模板&#xff0c;再将数据填充进去&#xff0c;这样可以保持设置好的样式&#xff0c;又能快速填充内容&#xff0c;十分方便。 智能…...

AcWing——糖果传递

有 n个小朋友坐成一圈&#xff0c;每人有 a[i]个糖果。 每人只能给左右两人传递糖果。 每人每次传递一个糖果代价为 1。 求使所有人获得均等糖果的最小代价。 输入格式 第一行输入一个正整数 n&#xff0c;表示小朋友的个数。 接下来 n 行&#xff0c;每行一个整数 a[i]&…...

Redis中的单线程模型

文章目录 文件事件处理器模型Redis的客户端与服务端的交互过程图Redis基于Reactor模式开发了自己的网络事件处理器,称之为 文件事件处理器(File Event Hanlder)。 文件事件处理器由Socket、IO多路复用程序文件事件分派器(dispather)事件处理器(handler)文件事件处理器模型 IO…...

Python函数默认参数设置(超级详细)

我们知道&#xff0c;在调用函数时如果不指定某个参数&#xff0c;Python 解释器会抛出异常。为了解决这个问题&#xff0c;Python 允许为参数设置默认值&#xff0c;即在定义函数时&#xff0c;直接给形式参数指定一个默认值。这样的话&#xff0c;即便调用函数时没有给拥有默…...

人工智能如何赋能业务创新?安克创新有话要说

对于一家企业来说&#xff0c;应该如何运用人工智能技术助力业务创新&#xff1f;作为一家多年复合增长率超过35%的企业&#xff0c;安克创新对这个话题无疑有着深切的体验感悟。飞速成长的消费电子企业众所周知&#xff0c;当下各行各业都在如火如荼地开展人工智能应用&#x…...

如何学习与学习的本质

如何学习两种模式两种记忆方式拖延问题学习方法学习本质两种模式 专注模式发散模式 专注模式和发散模式可以进行切换&#xff0c;提高效率&#xff0c; 发散模式可以后台工作。 两种记忆方式 工作记忆&#xff08;前额叶皮质&#xff09;长时记忆&#xff08;图像比较容易记…...

C++ deque容器

C deque容器 文章目录C deque容器前言1. deque容器基本概念2. deque构造函数3. deque赋值操作4. deque大小操作5. deque 插入和删除6. deque 数据存取7. deque 排序总结前言 本文包含deque容器基本概念、deque构造函数、deque赋值操作、deque大小操作、deque插入和删除、deque…...

HashMap的底层原理

hashmap是一个以key,value形式存储的集合,在JDK1.7中是以数组链表的数据结构,在JDK1.8中是数组链表红黑树的数据结构,他在对数据操作时继承了数组的线性查找和链表的寻址修改 hashmap是线程不安全的 : 在JDK1.7中会造成环形链和数据丢失的情况 在JDK1.8中hashmap的put过程会造…...

Django 4.0文档学习(四)

上篇文章 Django 4.0文档学习&#xff08;四&#xff09; 文章目录编写你的第一个 Django 应用&#xff0c;第 6 部分自定义应用的界面和风格编写你的第一个 Django 应用&#xff0c;第 7 部分自定义后台表单自定义后台更改列表自定义后台界面和风格自定义后台主页编写你的第一…...