设计模式之组合模式:探索对象组合的动态管理与操作技巧

一、什么是组合模式

        组合模式(Composite Pattern)是一种结构型模式(Structural Pattern),它主要解决的是如何将对象组合成树状以表示“部分-整体”的层次结构,并且可以对整个树进行统一的操作,如遍历、添加、删除等。组合模式的核心思想是将对象结构(树形结构)中的对象(包括叶子节点和容器节点)都看作同一类型的对象,从而使得客户端可以一致地处理单个对象和对象组合。组合模式主要由以下四个角色组成:

  1. Component(组件接口):定义了组合中所有对象的通用接口,可以是抽象类或接口。它声明了用于访问和管理子组件的方法,包括添加、删除、获取子组件等。

  2. Leaf(叶子节点):实现Component接口,代表树结构中的叶节点,没有子节点。叶子节点通常表示不可再分的对象。

  3. Composite(容器节点):实现Component接口,包含子组件(Component对象),并提供管理子组件的方法(如添加、删除、遍历等)。容器节点可以包含多个子节点,这些子节点可以是叶子节点,也可以是其他容器节点。

  4. Client(客户端):通过Component接口与对象结构进行交互,无需关心处理的是单个对象还是对象组合,可以一致地对待整体和部分。

二、组合模式的应用场景

        组合模式在实际项目中应用非常广泛,主要适用于以下场景:

  1. 树形结构处理:如文件系统、XML/HTML文档结构、组织架构、菜单系统等。

  2. 递归算法实现:组合模式可以简化递归算法的实现,如遍历树形结构、计算树结构的某些属性等。

  3. 一致的处理方式:当需要对单个对象和对象组合进行相同操作时,使用组合模式可以使处理逻辑更加一致。

  4. 动态组合:当系统需要在运行时动态地添加或删除树形结构的节点时,组合模式可以提供更好的灵活性。

三、组合模式示例

        以我们常见的人员组织树为例,一个公司内部会有多个部门,部门内部又可以进一步划分出其他部门,而人员则作为每个部门的叶子节点,所以,人员组织树很明显就很适合采用组合模式来实现。首先,我们定义一个抽象的组件类Entry,该类包含一个私有的属性name,并定义了抽象的方法添加、移除组件,另外还定义了一个方法用于在当前部门下查找某个用户的相对路径,其代码实现为:

public abstract class Entry {private String name;
​public abstract void add(Entry entry);
​public abstract void remove(Entry entry);
​/*** 查找并返回某用户在某部门下的相对路径集合*/public abstract List<String> find(String currPath, String name);
​public List<String> find(String name){return find("", name);}
​public String getName() {return name;}public void setName(String name) {this.name = name;}
}

        然后分别定义部门类Dept和人员类User,分别继承抽象类Entry,部门类内部定义子部门列表entries,并且部门类和人员类分别实现抽象类定义的方法:

部门类:

public class Dept extends Entry{private final List<Entry> entries = new ArrayList<>();
​public Dept(String name) {this.setName(name);}
​@Overridepublic void add(Entry entry) {entries.add(entry);}
​@Overridepublic void remove(Entry entry) {entries.remove(entry);}
​@Overridepublic List<String> find(String currPath, String name) {List<String> result = new ArrayList<>();String nextPath = currPath + getName() + "/";for (Entry entry : entries){result.addAll(entry.find(nextPath, name));}return result;}
​
}

人员类:

public class User extends Entry{public User(String name) {this.setName(name);}
​@Overridepublic void add(Entry entry) {
​}
​@Overridepublic void remove(Entry entry) {
​}
​@Overridepublic List<String> find(String currPath, String name) {if(this.getName().contains(name)){return Collections.singletonList(currPath + this.getName());}else{return new ArrayList<>();}}
​
}

        通过如上代码,我们便以组合模式实现了一个简单人员部门树结构,接下来我们写一个客户端类进行测试:

public class Client {public static void main(String[] args) {Dept root = new Dept("root");Dept dept1 = new Dept("dept1");Dept dept2 = new Dept("dept2");Dept dept3 = new Dept("dept3");
​User user1 = new User("user1");User user2 = new User("user2");User user3 = new User("user3");User user4 = new User("user4");User user5 = new User("user5");
​dept1.add(user1);dept2.add(user2);dept2.add(user4);dept2.add(user5);dept3.add(user3);dept3.add(user4);
​root.add(dept1);root.add(dept2);dept2.add(dept3);System.out.println(root.find("user4"));System.out.println(dept2.find("user4"));System.out.println(dept3.find("user4"));}
}

        在以上Client类中,我们构造了一个简单的人员部门树结构:

        通过find方法,分别在root、dept2、dept3下查找name为user4的用户,并打印其相对路径,执行结果为:

四、优化

        通过上述示例,我们已经实现了一个简单的组合模式,但是可以发现,由于添加、删除这种用于管理部门结构的方法是定义在抽象的Entry类中的,所有的构件类都有相同的方法,这种实现方式称为透明组合模式。而User类内部不可能再包含成员对象了,所以其add和remove方法实际上是没有意义的,在后续使用该组合模式的实现的时候,如果错误地使用了add和remove的方法的话,就可能产生一些预期外的错误,所以这种实现方式是不安全的。为了避免这种问题,我们可以将add和remove方法挪到Dept类中,这样就避免了错误地使用构件的方法产生的不安全的问题,而这种实现方式可以称为安全组合模

        而安全组合模式也不是一定强于透明组合模式的,二者各有优劣,在透明模式中,由于其安全性不足,在使用时可能会产生一些问题;而在安全模式中,由于其add和remove方法未在Entry类中定义,导致在开发时Dept和User类必须区别地对待,无法完全针对抽象编程,不够透明。简单来说,二者在安全和透明性上各有取舍,在使用组合模式时,要具体分析使用场景,选择合适的实现方式。

        下面给出安全组合模式的实例代码:

    Entry类:

public abstract class Entry {private String name;
​/*** 查找并返回某用户在某部门下的相对路径集合*/public abstract List<String> find(String currPath, String name);
​public List<String> find(String name){return find("", name);}
​public String getName() {return name;}public void setName(String name) {this.name = name;}
}

    Dept类:

public class Dept extends Entry {private final List<Entry> entries = new ArrayList<>();
​public Dept(String name) {this.setName(name);}
​public void add(Entry entry) {entries.add(entry);}
​public void remove(Entry entry) {entries.remove(entry);}
​@Overridepublic List<String> find(String currPath, String name) {List<String> result = new ArrayList<>();String nextPath = currPath + getName() + "/";for (Entry entry : entries){result.addAll(entry.find(nextPath, name));}return result;}
​
}

    User类:

public class User extends Entry {public User(String name) {this.setName(name);}
​@Overridepublic List<String> find(String currPath, String name) {if(this.getName().contains(name)){return Collections.singletonList(currPath + this.getName());}else{return new ArrayList<>();}}
​
}

    Client类作为测试类,不需进行改动:

public class Client {public static void main(String[] args) {Dept root = new Dept("root");Dept dept1 = new Dept("dept1");Dept dept2 = new Dept("dept2");Dept dept3 = new Dept("dept3");
​User user1 = new User("user1");User user2 = new User("user2");User user3 = new User("user3");User user4 = new User("user4");User user5 = new User("user5");
​dept1.add(user1);dept2.add(user2);dept2.add(user4);dept2.add(user5);dept3.add(user3);dept3.add(user4);
​root.add(dept1);root.add(dept2);dept2.add(dept3);System.out.println(root.find("user4"));System.out.println(dept2.find("user4"));System.out.println(dept3.find("user4"));}
}

        安全组合模式的实现的测试结果和透明组合模式的实现的测试结果是一致的:

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Redis过期删除策略和内存淘汰策略有什么区别?

Redis过期删除策略和内存淘汰策略有什么区别&#xff1f; 前言过期删除策略如何设置过期时间&#xff1f;如何判定 key 已过期了&#xff1f;过期删除策略有哪些&#xff1f;Redis 过期删除策略是什么&#xff1f; 内存淘汰策略如何设置 Redis 最大运行内存&#xff1f;Redis 内…

住宅IP代理和数据中心/机房IP代理之间的区别

一、什么是数据中心/机房IP代理&#xff1f; 数据中心/机房IP代理是使用数据中心拥有并进行分配和管理的IP的代理&#xff0c;俗称机房IP代理。 二、数据中心/机房IP代理的特点 与住宅代理通过使用ISP拥有和分配的IP地址的设备路由请求的情况不同&#xff0c;数据中心代理利…

【Cesium】Cesium核心类、坐标系与着色器简介

核心类&#xff1a; Viewer: Viewer 是 Cesium 中最基本的视图容器&#xff0c;用于显示地球、地图、三维场景等。它提供了创建和管理场景的功能&#xff0c;可以配置视图的各种属性和行为。 Scene: Scene 是 Cesium 中的核心类之一&#xff0c;代表了一个三维场景&#xff0c…

Collection工具类

Collection工具类的介绍 Collection 是一个操作Set、List和Map等集合的工具类Collection中提供了一些列静态的方法对集合元素进行排序、查询和修改的等操作 Collection的排序操作&#xff08;均为Static方法&#xff09; 1&#xff0c;reverse&#xff08;List&#xff09;&…

《A data independent approach to generate adversarial patches》论文分享(侵删)

原文链接&#xff1a;A data independent approach to generate adversarial patches | Machine Vision and Applications author{Xingyu Zhou and Zhisong Pan and Yexin Duan and Jin Zhang and Shuaihui Wang}, 一、介绍 在图像识别领域&#xff0c;与数字域中的攻击相比…

【教学类-55-03】20240512图层顺序挑战(三角形版)(6块三角形,420种叠放顺序)

作品展示 背景需求 分享Lab&#xff5c;更新啦&#xff5e;图层顺序挑战游戏 - 小红书 (xiaohongshu.com)https://www.xiaohongshu.com/discovery/item/62f21760000000000900ec6d?app_platformandroid&ignoreEngagetrue&app_version8.35.0&share_from_user_hidde…

【深入理解MySQL的索引数据结构】

文章目录 &#x1f50a;博主介绍&#x1f964;本文内容&#x1f4d5;索引底层数据结构与算法&#x1f4d9;索引数据结构&#x1f4d8;二叉树&#x1f4d8;红黑树&#x1f4d8;Hash&#x1f4d8;B-Tree&#x1f4d8;BTree &#x1f4d9;表在不同存储引擎的存储结构&#x1f4d8;…

Unity编辑器如何多开同一个项目?

在联网游戏的开发过程中&#xff0c;多开客户端进行联调是再常见不过的需求。但是Unity并不支持编辑器多开同一个项目&#xff0c;每次都得项目打个包(耗时2分钟以上)&#xff0c;然后编辑器开一个进程&#xff0c;exe 再开一个&#xff0c;真的有够XX的。o(╥﹏╥)o没错&#…

Kotlin: ‘return‘ is not allowed here

报错&#xff1a;以下函数的内部函数return语句报错 Kotlin: return is not allowed here fun testReturn(summary: (String) -> String): String {var msg summary("summary收到参数")println("test内部调用参数&#xff1a;>结果是 &#xff1a;${msg…

大屏分辨率适配插件v-scale-screen

前言&#xff1a;大屏分辨率适配繁多&#xff0c;目前我认为最简单且问题最少的的方案就是使用v-scale-screen插件&#xff0c;无需考虑单位转换&#xff0c;position定位也正常使用。 1. 效果 填充满屏幕的效果 保持宽高比的效果 2. 插件原理 原理是通过css transfom 实现…

c++ map,set封装

map 是一个 kv 结构&#xff0c; set 是 k结构。 我们前面模拟实现了 红黑树&#xff0c;但是我们实现的红黑树把 kv 结构写死了&#xff0c;怎么样才能用泛型编程的思想来实现map和set呢 我们先简单看一下原码中是怎么实现的 1.原码实现逻辑 我们打开这里的 stl_set.h 通过…

淘宝闲鱼卖复印机,日入2000,2024年全新教程

1、项目概述 今天&#xff0c;我要向大家介绍一个在淘宝闲鱼上进行的复印机买卖项目。随着科技的快速发展&#xff0c;电子产品的更新换代速度加快&#xff0c;许多公司每年都需要更换新的复印机&#xff0c;而旧的复印机通常会被转售到二手市场&#xff0c;其中淘宝闲鱼是最大…