设计模式——组合模式

生活中存在很多 “部分-整体” 的关系,例如:大学中的学校与学院、学院与专业的关系。高楼与楼层和房间之间的关系等等。在软件开发中也有类似的情况。这些简单对象与复合对象之间的关系,如果用组合模式(把学校、院、系都看作是组织结构,他们之间没有继承的关系,而是一种树形结构,可以更好的实现管理操作)来实现会很方便。

一、基本介绍

1)、组合模式(Composite Pattern):又叫部分整体模式,它创建了对象组的树形结构,将对象组合成树状结构以表示“整体-部分”的层次关系。
2)、组合模式依据树形结构来组合对象,用来表示部分以及整体层次。
3)、这种设计模式属于结构型模式。
4)、组合模式使得用户对单个对象和组合对象的访问具有一致性,即:组合能让客户以一致的方式处理个别对象以及组合对象。
5)、优点:组合模式使得客户端代码可以一致地处理单个对象和组合对象,无需关系自己处理的是单个对象,还是组合对象,这简化了客户端代码;更容易在组合体内加入新的对象,客户端不会因为加入了新的对象而更改源代码,满足 “开闭原则 OCP”。
6)、缺点:设计复杂,客户端需要花更多时间理清类之间的层次关系。不容易限制容器中构建,不容易用继承的方式增加构建的新功能。

二、组合模式——结构类图

组合模式分为透明式的组合模式和安全式组合模式:

 1)、透明方式:在该方法中,由于抽象构建声明了所有子类中的全部方法,所以客户端无需区别树叶对象和树枝对象。对客户端来说是透明的。其缺点是:树叶构件本来没有 Add()、Remove() 及 GetChild() 方法,却要实现它们(空实现或抛异常),这样会带来一些安全性问题。

 2)、安全方式:在该方式中,将管理子构件的方法移到树枝构件中,抽象构件和树叶构件没有对子对象的管理方法,这样就避免了上一种方式的安全性问题,但由于叶子和分支有不同的接口,客户端在调用时要知道树叶对象和树枝对象的存在,所以失去了透明性。

三、组合模式代码案例分析

【1】抽象构件(Component)角色: 主要作用是为树叶和树枝构件生命公共接口,并实现他们的默认行为。在透明式的组合模式中,抽象构件还声明访问和管理子类的接口(add/remove)。在安全式的组合模式中不声明访问和管理子类的接口,管理工作由树枝构建完成。

public abstract class AbstractComponent {//学校的名称private String name;//备注private String remark;//定义一个输入的抽象方法public abstract void output();//非叶子节点都具有的增加和删除方法public void add(AbstractComponent abstractComponent) {//当重写此方法直接调用时,就会抛出此异常。与Arrays.asList()内部类中的add与remove写法一致throw new UnsupportedOperationException();}public void remove(AbstractComponent abstractComponent) {throw new UnsupportedOperationException();}//构造器public AbstractComponent(String name, String remark) {super();this.name = name;this.remark = remark;}//get/set方法 tostring方法 略}
}

【2】树枝构件(Composite)角色: 是组合中的分支节点对象,它有子节点。它实现了抽象构件角色中声明的接口,它的主要作用是

public class University extends AbstractComponent{//构造器public University(String name, String remark) {super(name, remark);}//大学中包含多个学院List<AbstractComponent> college = new ArrayList<AbstractComponent>();//重写输出方法:输出叶子节点@Overridepublic void output() {System.out.println("===="+super.getName()+"====");for (AbstractComponent abstractComponent : college) {abstractComponent.output();}}//组合类也就是树枝节点,需要重写add与remove方法@Overridepublic void add(AbstractComponent abstractComponent) {college.add(abstractComponent);}@Overridepublic void remove(AbstractComponent abstractComponent) {college.remove(abstractComponent);}
}

【3】学院类也是树枝构件(Composite)角色,与上述基本一致。

public class College extends AbstractComponent{//构造器public College(String name, String remark) {super(name, remark);}List<AbstractComponent> list = new ArrayList<>();//输入@Overridepublic void output() {for (AbstractComponent abstractComponent : list) {abstractComponent.output();}}//重写添加和删除方法@Overridepublic void add(AbstractComponent abstractComponent) {list.add(abstractComponent);}@Overridepublic void remove(AbstractComponent abstractComponent) {list.remove(abstractComponent);}
}

【4】树叶构件(Leaf)角色: 是组合中的叶节点对象,它没有子节点,用于实现抽象构件角色中 声明的公共接口。

public class Major extends AbstractComponent{//构造器public Major(String name, String remark) {super(name, remark);}//add , remove 就不用写了,因为他是叶子节点//输入@Overridepublic void output() {System.out.println(getName());}}

【5】测试类

public class Client {public static void main(String[] args) {//定义一个大学AbstractComponent university = new University("浙江大学", "浙江人的骄傲");//定义一个学院AbstractComponent college = new College("计算机学院", "简称妓院");//将妓院添加至学校university.add(college);//定义一个专业AbstractComponent major = new Major("计算机科学与技术", "考研大专业");//添加至学院college.add(major);//输出:计算机科学与技术 输入的都是叶子节点的output方法major.output();college.output();university.output();}
}

四、组合模式源码分析

【1】HashMap 组合模式:首先定义了抽象构建角色 Map<K,V>

public interface Map<K,V> {....}

【2】使用接口适配器模式,定义了抽象实现:AbstractMap

public abstract class AbstractMap<K,V> implements Map<K,V> {public V put(K key, V value) {//由子类实现throw new UnsupportedOperationException();}......
}

【3】HashMap等实现类属于树枝构建角色:组合了Node叶子节点类

public class HashMap<K,V> extends AbstractMap<K,V>implements Map<K,V>, Cloneable, Serializable {public V put(K key, V value) {return putVal(hash(key), key, value, false, true);}public void putAll(Map<? extends K, ? extends V> m) {putMapEntries(m, true);}......final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {Node<K,V>[] tab; Node<K,V> p; int n, i;......}
}

【4】Node 类是上述类的内部类,也就是组合模式中的树叶构建:数组存储时,通过 put 方法存入 Node 对象中,叶子节点 Node 中不包含添加方法等,只包含一些属性和其 get/set 方法

public class HashMap<K,V> extends AbstractMap<K,V>implements Map<K,V>, Cloneable, Serializable {......static class Node<K,V> implements Map.Entry<K,V> {final int hash;final K key;V value;Node<K,V> next;Node(int hash, K key, V value, Node<K,V> next) {this.hash = hash;this.key = key;this.value = value;this.next = next;}public final K getKey()        { return key; }public final V getValue()      { return value; }public final String toString() { return key + "=" + value; }public final int hashCode() {return Objects.hashCode(key) ^ Objects.hashCode(value);}public final V setValue(V newValue) {V oldValue = value;value = newValue;return oldValue;}public final boolean equals(Object o) {if (o == this)return true;if (o instanceof Map.Entry) {Map.Entry<?,?> e = (Map.Entry<?,?>)o;if (Objects.equals(key, e.getKey()) &&Objects.equals(value, e.getValue()))return true;}return false;}}......
}

五、组合模式的注意事项和细节

 ☛ 简化客户端操作。客户端只需要面对一致的对象而不用考虑整体部分或者节点叶子的问题。
 ☛ 具有较强的扩展性。当我们要更改组合对象时,我们只需要调整内部的层次关系,客户端不用做任何改动。
 ☛ 方便创建出复杂的层次结构。客户端不用理会组合里面的组成细节,容器添加节点或者叶子。从而创建出复杂的树形结构。
 ☛ 需要遍历组织机构,或者处理的对象具有树形结构时,非常适合使用组合模式。
 ☛ 当要求较高的抽象性时,如果节点和叶子有很多差异的话,例如很多方法和属性都不一样,不适合使用组合模式。

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

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

相关文章

链表练习 Leetcode82.删除排序链表中的重复元素 II

题目传送门&#xff1a;Leetcode82 给定一个已排序的链表的头 head &#xff0c; 删除原始链表中所有重复数字的节点&#xff0c;只留下不同的数字 。返回 已排序的链表 。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,3,4,4,5] 输出&#xff1a;[1,2,5]示例 2&#xff1…

【Linux】Linux 系统编程——cd 命令

文章目录 1.命令概述2.命令格式3.常用选项4.相关描述5.参考示例 1.命令概述 “cd 命令&#xff0c;即 ‘change directory’ 的缩写&#xff0c;主要用于 Unix、Linux 和 macOS 等操作系统中&#xff0c;用于改变当前工作目录。该命令支持绝对路径和相对路径两种形式。若未指定…

SpringBoot多环境配置与添加logback日志

1、多环境配置 一个项目会有多个运行环境 所以SpringBoot提供了可以适应多个环境的配置文件 每个文件对应一个端口号 application-dev.yml 开发环境 端口8090 application-test.yml 测试环境 端口8091 application-prod.yml 生产环境 端口8092 在application中选择使用哪个…

线性回归实例

1、线性回归&#xff08;linear Regression&#xff09;和逻辑回归&#xff08;logistic Regression&#xff09;的区别 线性回归主要是用来拟合数据&#xff0c;逻辑回归主要是用来区分数据&#xff0c;找到决策边界。 线性回归的代价函数常用平方误差函数&#xff0c;逻辑回…

AI在保护环境、应对气候变化中的作用

对于AI生命周期数据领域的全球领导者而言&#xff0c;暂时搁置我们惯常的AI见解和AI生命周期数据内容产出&#xff0c;来认识诸如世界地球日这样的自然环境类活动日&#xff0c;似乎是个奇怪的事情。我们想要知道&#xff0c;数据是否真的会影响我们的地球环境&#xff1f; 简而…

5.5G来了!全球首次采用,就在福建

近日&#xff0c;在以滨海风景优美而闻名的福建厦门马拉松赛道沿线区域&#xff0c;福建移动携手华为采用5G-A通感一体技术率先完成了低空多站连续组网的通感能力验证&#xff0c;基于4.9GHz频段实现了对城市低空120米下多无人驾驶飞行器的探测与及时预警。这一成果为城市低空资…

【windows】右键添加git bash here菜单

在vs 里安装了git for windows 后&#xff0c;之前git-bash 右键菜单消失了。难道是git for windows 覆盖了原来自己安装的git &#xff1f;大神给出解决方案 手动添加Git Bash Here到右键菜单&#xff08;超详细&#xff09; 安装路径&#xff1a;我老的 &#xff1f; vs的gi…

Kafka系列(四)

本文接kafka三&#xff0c;代码实践kafkaStream的应用&#xff0c;用来完成流式计算。 kafkastream 关于流式计算也就是实时处理&#xff0c;无时间概念边界的处理一些数据。想要更有性价比地和java程序进行结合&#xff0c;因此了解了kafka。但是本人阅读了kafka地官网&#…

论文笔记:信息融合的门控多模态单元(GMU)

整理了GMU&#xff08;ICLR2017 GATED MULTIMODAL UNITS FOR INFORMATION FUSION&#xff09;论文的阅读笔记 背景模型实验 论文地址&#xff1a; GMU 背景 多模态指的是同一个现实世界的概念可以用不同的视图或数据类型来描述。比如维基百科有时会用音频的混合来描述一个名人…

vue+echarts 几个案例

普通柱状图 <template><!-- 容器默认宽高是0 如果不设置 页面不显示--><div ref"mychart" id"mychart"></div> </template><script> import * as echarts from "echarts" import axios from axiosexport …

(Matlab)基于CNN-Bi_LSTM的多维回归预测(卷积神经网络-双向长短期记忆网络)

目录 一、程序及算法内容介绍&#xff1a; 基本内容&#xff1a; 亮点与优势&#xff1a; 二、实际运行效果&#xff1a; 三、部分代码展示&#xff1a; 四、完整程序数据分享&#xff1a; 一、程序及算法内容介绍&#xff1a; 基本内容&#xff1a; 本代码基于Matlab平…

数字电源简介

数字电源简介 定义主要应用场景数字电源的基本组成常见算法常见电源拓扑PFCLLC 数字电源与模拟电源对比参考链接 定义 常见定义有以下四种&#xff1a; 通过数字接口控制的开关电源&#xff0c;强调的是数字电源的“通信”功能。可通过I2C或类似的数字总线来对数字信号进行控…

springboot整合websocket后启动报错:javax.websocket.server.ServerContainer not available

一、场景 Springboot使用ServerEndpoint来建立websocket链接。引入依赖。 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency>配置Websocket Confi…

九:day01_ 消息队列01

第一章 RabbitMQ 概念 1.1.1 消息队列 MQ全称Message Queue&#xff08;消息队列&#xff09;&#xff0c;是在消息的传输过程中保存消息的容器。多用于系统之间的异步通信。 1.1.2 消息 两台计算机间传送的数据单位。消息可以非常简单&#xff0c;例如只包含文本字符串&#x…

【目标检测】YOLOv5算法实现(九):模型预测

本系列文章记录本人硕士阶段YOLO系列目标检测算法自学及其代码实现的过程。其中算法具体实现借鉴于ultralytics YOLO源码Github&#xff0c;删减了源码中部分内容&#xff0c;满足个人科研需求。   本系列文章主要以YOLOv5为例完成算法的实现&#xff0c;后续修改、增加相关模…

深入解析JavaScript属性的getter和setter

&#x1f9d1;‍&#x1f393; 个人主页&#xff1a;《爱蹦跶的大A阿》 &#x1f525;当前正在更新专栏&#xff1a;《VUE》 、《JavaScript保姆级教程》、《krpano》、《krpano中文文档》 ​ ​ ✨ 前言 在JavaScript中,对象属性除了直接设置值之外,还可以通过getter和sett…

鸿蒙OS4.0兼容性测试

背景 OpenHarmony兼容性测评主要是验证合作伙伴的设备和业务应用满足OpenHarmony开源兼容性定义的技术要求&#xff0c;确保运行在OpenHarmony上的设备和业务应用能稳定、正常运行&#xff0c;同时使用OpenHarmony的设备和业务应用有一致性的接口和业务体验。 OpenHarmony兼容…

MongoDB调优

三大导致 MongoDB 性能不佳的原因 1&#xff09;慢查询2&#xff09;阻塞等待3&#xff09;硬件资源不足1,2通常是因为模型/索引设计不佳导致的。排查思路&#xff1a;按1-2-3依次排查。 影响 MongoDB 性能的因素 MongoDB 性能监控工具 Free Monitoring 从版本 4.0 开始&am…

redis经典面试题

说说你对Redis的理解 Redis是一个基于Key-Value存储结构的开源内存数据库&#xff0c;也是一种NoSQL数据库。 它支持多种数据类型&#xff0c;包括String、Map、Set、ZSet和List&#xff0c;以满足不同应用场景的需求。 Redis以内存存储和优化的数据结构为基础&#xff0c;提…

Python爬虫实战:IP代理池助你突破限制,高效采集数据

当今互联网环境中&#xff0c;为了应对反爬虫、匿名访问或绕过某些地域限制等需求&#xff0c;IP代理池成为了一种常用的解决方案。IP代理池是一个包含多个可用代理IP地址的集合&#xff0c;可以通过该代理池随机选择可用IP地址来进行网络请求。 IP代理池是一组可用的代理IP地址…