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

news/2024/9/22 12:25:36/

一、什么是组合模式

        组合模式(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.ppmy.cn/news/1459981.html

相关文章

FANUC机器人工具坐标偏移的用法

一、工具坐标偏移的使用场景 在机器人位置不改变的情况下&#xff0c;工业机器人使用默认工具坐标系示教的一系列运动点位&#xff0c;要保持原本点位位置不变的情况下&#xff0c;改变机器人工具坐标的参数&#xff0c;就要用到机器人坐标转化的功能。在FANUC机器人上体现为机…

无人机+人工智能:多智能体,智能蜂群技术详解

无人机与人工智能的结合&#xff0c;特别是在多智能体和智能蜂群技术方面&#xff0c;已经成为当今科技领域的前沿。这种技术的核心在于利用人工智能的决策和学习能力&#xff0c;结合无人机的机动性和传感器能力&#xff0c;实现一种高度协同、自主、智能的作战或任务执行方式…

【平台搭建+数据处理+数据可视化】

第一部分:大数据平台搭建 一、Hadoop 1、完全分布式Hadoop集群搭建: (1)在master主节点将/opt目录下的Hadoop安装包hadoop-3.1.4.tar.gz解压到/opt/software目录下。 mkdir -p /opt/software tar -zxf /opt/hadoop-3.1.4.tar.gz -C /opt/software (2)创建Hadoop临时数…

QRegExp

描述 QRegExp 类使用正则表达式提供模式匹配。 正则表达式或“正则表达式”是一种用于匹配文本中子字符串的模式。这在许多情况下都很有用&#xff0c;例如&#xff0c; 验证 正则表达式可以测试子字符串是否满足某些条件&#xff0c;例如是整数或不包含空格。搜索 正则表达式…

Android之给Button上添加按压效果

一、配置stateListAnimator参数实现按压效果 1、按钮控件 <Buttonandroid:id"id/mBtnLogin"android:layout_width"match_parent"android:layout_height"48dp"android:background"drawable/shape_jfrb_login_button"android:state…

Git === Git概述 Git安装

第1章 Git概述 Git是一个免费的、开源的分布式版本控制系统&#xff0c;可以快速高效地处理从小型到大型的各种项目。 Git易于学习&#xff0c;占地面积小&#xff0c;性能极快。 它具有廉价的本地库&#xff0c;方便的暂存区域和多个工作流分支等特性。其性能优于Subversion…

OpenAI不会发布GPT-5 及AI搜索引擎;苹果iOS 18将为备忘录应用带来AI升级

&#x1f989; AI新闻 &#x1f680; OpenAI不会发布GPT-5 及AI搜索引擎 摘要&#xff1a;OpenAI宣布将在5月13日进行网络直播&#xff0c;讲述ChatGPT升级内容。Sam Altman在X平台明确表示&#xff0c;下周一不会发布GPT-5和AI搜索引擎&#xff0c;但他强调公司正在开发一些…

EasyExcel简单使用

EasyExcel简单使用 ​ 之前一直用的Apache POI来做数据的导入导出&#xff0c;但听说阿里的EasyExcel也拥有POI的功能的同时&#xff0c;在处理大数据量的导入导出的时候性能上比POI更好&#xff0c;所以就来尝试使用一下 导入Maven依赖&#xff1a; <dependency><…