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

文章目录

    • 1、学校院系展示需求
    • 2、组合模式基本介绍
    • 3、组合模式示例
      • 3.1、 解决学校院系展示(透明模式1)
      • 3.2、高考的科目(透明模式2)
      • 3.3、高考的科目(安全组合模式)
    • 4、JDK 源码分析
    • 5、注意事项和细节

1、学校院系展示需求

编写程序展示一个学校院系结构:

需求是这样,要在一个页面中展示出学校的院系组成,一个学校有多个学院,一个学院有多个系。如图:
在这里插入图片描述
传统方式解决学校院系展示(类图)

在这里插入图片描述
问题分析

  • 1)将学院看做是学校的子类,系是学院的子类,这样实际上是站在组织大小来进行分层次的
  • 2)实际上我们的要求是:在一个页面中展示出学校的院系组成,一个学校有多个学院,一个学院有多个系。因此这种方案,不能很好实现的 管理 的操作,比如对学院、系的添加、删除、遍历等
  • 3)解决方案:把学校、院、系都看做是组织结构,他们之间没有继承的关系,而是一个树形结构,可以更好的实现管理操作 ==> 组合模式

2、组合模式基本介绍

  • 1)组合模式(Composite Pattern),又叫部分整体模式。它创建了对象组的树形结构,将对象组合成树状结构以表示“整体-部分”的层次关系

  • 2)组合模式依据树形结构来组合对象,用来表示部分以及整体层次

  • 3)这种类型的设计模式属于结构型模式

  • 4)组合模式使得用户对单个对象和组合对象的访问具有一致性,即:组合能让客户以一致的方式处理个别对象以及组合对象
    在这里插入图片描述
    对原理结构图的说明一即组合模式的角色及职责

  • 1)Component:这是组合中对象声明接口。在适当情况下,实现所有类共有的接口默认行为,用于访问和管理 Component子部件。Component可以是抽象类或者接口

  • 2)Leaf:在组合中表示叶子结点,叶子结点没有子节点

  • 3)Composite:非叶子结点,用于存储子部件,在Component接口中实现子部件的相关操作。比如增加、删除

解决的问题

组合模式解决这样的问题,当我们的要处理的对象可以生成一棵树形结构,而我们要对树上的节点和叶子进行操作时,它能够提供一致的方式,而不用考虑它是节点还是叶子

在这里插入图片描述

3、组合模式示例

组合模式有两种写法,分别是透明模式安全模式

3.1、 解决学校院系展示(透明模式1)

UML 类图
在这里插入图片描述

核心代码

// Component 抽象类
public abstract class OrganizationComponent {private String name;public OrganizationComponent(String name) {this.name = name;}public String getName() {return name;}public void setName(String name) {this.name = name;}public void add(OrganizationComponent organizationComponent) {throw new UnsupportedOperationException();}public void remove(OrganizationComponent organizationComponent) {throw new UnsupportedOperationException();}public abstract void print();
}
// Composite 非叶子节点
public class University extends OrganizationComponent {List<OrganizationComponent> organizationComponentList = new ArrayList<>();public University(String name) {super(name);}@Overridepublic void add(OrganizationComponent organizationComponent) {organizationComponentList.add(organizationComponent);}@Overridepublic void remove(OrganizationComponent organizationComponent) {organizationComponent.remove(organizationComponent);}@Overridepublic void print() {for (OrganizationComponent organizationComponent : organizationComponentList) {organizationComponent.print();}}
}
public class College extends OrganizationComponent {List<OrganizationComponent> organizationComponentList = new ArrayList<>();public College(String name) {super(name);}@Overridepublic void add(OrganizationComponent organizationComponent) {organizationComponentList.add(organizationComponent);}@Overridepublic void remove(OrganizationComponent organizationComponent) {organizationComponent.remove(organizationComponent);}@Overridepublic void print() {System.out.println("=============" + getName() + "=============");for (OrganizationComponent organizationComponent : organizationComponentList) {organizationComponent.print();}}
}
// Leaf 叶子结点
public class Major extends OrganizationComponent {public Major(String name) {super(name);}@Overridepublic void print() {System.out.println(getName());}
}
// 客户端
public class Client {public static void main(String[] args) {//大学OrganizationComponent university = new University("清华大学");//学院OrganizationComponent computerCollege = new College("计算机学院");OrganizationComponent infoEngineerCollege = new College("信息工程学院");//专业computerCollege.add(new Major("软件工程"));computerCollege.add(new Major("网络工程"));computerCollege.add(new Major("计算机科学与技术"));infoEngineerCollege.add(new Major("通信工程"));infoEngineerCollege.add(new Major("信息工程"));university.add(computerCollege);university.add(infoEngineerCollege);university.print();}
}

打印结果

  //=============计算机学院=============//软件工程//网络工程//计算机科学与技术//=============信息工程学院=============//通信工程//信息工程

3.2、高考的科目(透明模式2)

1、首先建立一个顶层的抽象科目类,这个类中定义了三个通用操作方法,但是均默认不支持操作

package com.zwx.design.pattern.composite.transparency;/*** 顶层抽象组件*/
public abstract class GkAbstractCourse {public void addChild(GkAbstractCourse course) {System.out.println("不支持添加操作");}public String getName() throws Exception {throw new Exception("不支持获取名称");}public void info() throws Exception {throw new Exception("不支持查询信息操作");}
}

PS:这个类中的公共方法之所以不定义为抽象方法的原因是因为假如定义为抽象方法,那么所有的子类都必须重写父类方法,这样体现不出差异性。而这种通过抛异常的方式,如果子类需要用到的功能就重写覆盖父类方法即可。

2、新建一个普通科目类继承通用科目抽象类,这个类作为叶子节点,没有重写addChild方法,也就是这个类属于叶子节点,不支持添加子节点:

package com.zwx.design.pattern.composite.transparency;/*** 普通科目类(叶子节点)*/
public class CommonCource extends GkAbstractCourse {private String name;private String score;public CommonCource(String name, String score) {this.name = name;this.score = score;}@Overridepublic String getName() {return this.name;}@Overridepublic void info() {System.out.println("课程:" + this.name + ",分数:" + score);}
}

3、建立一个具有层级的节点,三个方法都重写了,支持添加子节点,这个类里面为了方便打印的时候看出层级关系,所以我定义了一个层级属性。

package com.zwx.design.pattern.composite.transparency;import java.util.ArrayList;
import java.util.List;/*** 树枝节点*/
public class LevelCource extends GkAbstractCourse {private List<GkAbstractCourse> courseList = new ArrayList<>();private String name;private int level;public LevelCource(String name, int level) {this.name = name;this.level = level;}@Overridepublic void addChild(GkAbstractCourse course) {courseList.add(course);}@Overridepublic String getName() {return this.name;}@Overridepublic void info() throws Exception {System.out.println("课程:" + this.name);for (GkAbstractCourse course : courseList) {for (int i = 0; i < level; i++) {System.out.print(" ");}System.out.print(">");course.info();}}
}

4、建立一个测试类来测试一下:

package com.zwx.design.pattern.composite.transparency;public class TestTransparency {public static void main(String[] args) throws Exception {GkAbstractCourse ywCourse = new CommonCource("语文", "150");GkAbstractCourse sxCourse = new CommonCource("数学", "150");GkAbstractCourse yyCourse = new CommonCource("英语", "150");GkAbstractCourse wlCourse = new CommonCource("物理", "110");GkAbstractCourse hxCourse = new CommonCource("化学", "100");GkAbstractCourse swCourse = new CommonCource("生物", "90");GkAbstractCourse lzCourse = new LevelCource("理综", 2);lzCourse.addChild(wlCourse);lzCourse.addChild(hxCourse);lzCourse.addChild(swCourse);GkAbstractCourse gkCourse = new LevelCource("理科高考科目", 1);gkCourse.addChild(ywCourse);gkCourse.addChild(sxCourse);gkCourse.addChild(yyCourse);gkCourse.addChild(lzCourse);gkCourse.info();}
}

输出结果:

课程:理科高考科目  
> 课程:语文,分数:150  
> 课程:数学,分数:150  
> 课程:英语,分数:150  
> 课程:理综  >课程:物理,分数:110  >课程:化学,分数:100  >课程:生物,分数:90

这里如果用普通科目去调用add方法就会抛出异常,假如上面调用:

swCourse.addChild(ywCourse);

会输出

不支持添加操作

因为在普通科目类里面并没有重写addChild方法。

透明组合模式的缺陷

透明模式的特点就是将组合对象所有的公共方法都定义在了抽象组件内,这样做的好处是客户端无需分辨当前对象是属于树枝节点还是叶子节点,因为它们具备了完全一致的接口,不过缺点就是叶子节点得到到了一些不属于它的方法,比如上面的addChild方法,这违背了接口隔离性原则

3.3、高考的科目(安全组合模式)

安全组合模式只是规定了系统各个层次的最基础的一致性行为,而把组合(树节点)本身的方法(如树枝节点管理子类的addChild等方法)放到自身当中。

1、首先还是建立一个顶层的抽象根节点(这里面只定义了一个通用的抽象info方法):

package com.zwx.design.pattern.composite.safe;package com.zwx.design.pattern.composite.safe;/*** 顶层抽象组件*/
public abstract class GkAbstractCourse {protected String name;protected String score;public GkAbstractCourse(String name, String score) {this.name = name;this.score = score;}public abstract void info();
}

2、建立一个叶子节点(这里只是重写了info方法,没有定义其他特有方法):

package com.zwx.design.pattern.composite.safe;/*** 叶子节点*/
public class CommonCource extends GkAbstractCourse {public CommonCource(String name, String score) {super(name, score);}@Overridepublic void info() {System.out.println("课程:" + this.name + ",分数:" + this.score);}
}

3、定义一个树枝节点(这个类当中定义了一个树枝特有的方法addChild):

package com.zwx.design.pattern.composite.safe;import java.util.ArrayList;
import java.util.List;/*** 树枝节点*/
public class LevelCource extends GkAbstractCourse {private List<GkAbstractCourse> courseList = new ArrayList<>();private int level;public LevelCource(String name, String score, int level) {super(name, score);this.level = level;}public void addChild(GkAbstractCourse course) {courseList.add(course);}@Overridepublic void info() {System.out.println("课程:" + this.name + ",分数:" + this.score);for (GkAbstractCourse course : courseList) {for (int i = 0; i < level; i++) {System.out.print(" ");}System.out.print(">");course.info();}}
}

4、新建测试类来测试:

package com.zwx.design.pattern.composite.safe;public class TestSafe {public static void main(String[] args) throws Exception {CommonCource ywCourse = new CommonCource("语文", "150");CommonCource sxCourse = new CommonCource("数学", "150");CommonCource yyCourse = new CommonCource("英语", "150");CommonCource wlCourse = new CommonCource("物理", "110");CommonCource hxCourse = new CommonCource("化学", "100");CommonCource swCourse = new CommonCource("生物", "90");LevelCource lzCourse = new LevelCource("理综", "300", 2);lzCourse.addChild(wlCourse);lzCourse.addChild(hxCourse);lzCourse.addChild(swCourse);LevelCource gkCourse = new LevelCource("理科高考", "750", 1);gkCourse.addChild(ywCourse);gkCourse.addChild(sxCourse);gkCourse.addChild(yyCourse);gkCourse.addChild(lzCourse);gkCourse.info();}
}

这里和透明方式不一样,叶子节点不具备addChild功能,所以无法调用,而上面的示例中时可以被调用,但是调用之后显示不支持,这就是这两种写法最大的区别。

组合模式角色

从上面示例中,可以看到组合模式包含了以下三个角色:

  • 抽象根节点(Component):定义系统各层次对象的公有属性和方法,可以预先定义一些默认行为和属性。
  • 树枝节点(Composite):定义树枝节点的行为,存储子节点,组合树枝节点和叶子节点形成一个树形结构。
  • 叶子节点(Leaf):是系统遍历层次中的最小单位,下面没有子节点。

4、JDK 源码分析

Java 的集合类—— HashMap 就使用了组合模式

UML 类图

在这里插入图片描述
核心代码

// Component
public interface Map<K,V> {interface Entry<K,V> {}
}
public abstract class AbstractMap<K,V> implements Map<K,V> {}
// Composite
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable {// Leafstatic class Node<K,V> implements Map.Entry<K,V> {}
}

说明

  • 1)Map 就是一个抽象的构建,类似Component
  • 2)HashMap 是一个中间的构建,类似Composite,实现 / 继承了相关方法 put、putAll
  • 3)Node 是 HashMap 的静态内部类,类似Leaf叶子节点,这里就没有 put

5、注意事项和细节

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

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

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

相关文章

存储过程编程-创建(CREATE PROCEDURE)、执行(EXEC)、删除(DROP PROCEDURE)

一、定义 1、存储过程是在SQL服务器上存储的已经编译过的SQL语句组。 2、存储过程分为三类&#xff1a;系统提供的存储过程、用户定义的存储过程和扩展存储过程 &#xff08;1&#xff09;系统提供的存储过程&#xff1a;在安装SQL Server时&#xff0c;系统创建了很多系统存…

AI机器人在企业拓客上常见的功能有哪些

AI机器人具备多种功能&#xff0c;这些功能主要基于其被设计和训练的目的。整理了一些常见的AI机器人功能&#xff1a; 1. 语音识别与自然语言处理&#xff1a; - 语音识别&#xff1a;将用户的语音输入转换为文本&#xff0c;以便机器人可以理解和处理。 - 自然语言处理…

QCC5181 歌词歌曲名多国语言显示替代QCC5125 CSR8675

QCC518X作为Qualcomm新一代蓝牙技术芯片&#xff0c;支持最新蓝牙协议V5.4&#xff0c;较QCC512X系列&#xff0c;它有更强大的DSP、CPU。除支持USB、I2S、SPDIF等接口外&#xff0c;还扩展了LE Audio功能&#xff0c;扩展支持AptX Lossless。以5181为例&#xff0c;我们还扩展…

vscode语言模式

1.背景 写vue3ts项目的时候&#xff0c;用到了volar插件&#xff0c;在单文件使用的时候&#xff0c;鼠标悬浮在代码上面会有智能提示&#xff1b; 但是最近volar插件提示被弃用了&#xff0c;然后我按照它的官方提示&#xff0c;安装了Vue-official扩展插件&#xff0c;但是…

Banana Pi BPI-M5 Pro 低调 SBC 采用 Rockchip RK3576 八核 Cortex-A72/A53 AIoT SoC

Banana Pi BPI-M5 Pro&#xff0c;也称为 Armsom Sige5&#xff0c;是一款面向 AIoT 市场的低调单板计算机 (SBC)&#xff0c;由 Rockchip RK3576 八核 Cortex-A72/A53 SoC 驱动&#xff0c;提供Rockchip RK3588和RK3399 SoC 之间的中档产品。 该主板默认配备 16GB LPDDR4X 和…

如何大幅减少 Vue.js 中的包大小和加载时间,提升用户体验!

大家好,我是CodeQi! 一位热衷于技术分享的码仔。 你知道吗,根据Google 的一项研究,如果网站加载时间超过 3 秒,53% 的移动用户会离开该网站? 性能优化是一个经常讨论的话题,但很多开发人员并不关心提高应用的速度。 在前端开发中,优化包大小和加载时间对于提升用户体…

下一代 CLI 工具,使用Go语言用于构建令人惊叹的网络应用程序

大家好&#xff0c;今天给大家分享一个创新的命令行工具Gowebly CLI&#xff0c;它专注于使用Go语言来快速构建现代Web应用程序。 Gowebly CLI 是一款免费开源软件&#xff0c;有助于在后端使用 Go、在前端使用 htmx 和 hyperscript 以及最流行的 CSS 框架轻松构建令人惊叹的 W…

入门PHP就来我这(高级)15 ~ 图书删除功能

有胆量你就来跟着路老师卷起来&#xff01; -- 纯干货&#xff0c;技术知识分享 路老师给大家分享PHP语言的知识了&#xff0c;旨在想让大家入门PHP&#xff0c;并深入了解PHP语言。 今天给大家接着上篇文章实现图书删除功能&#xff0c;来实现删除图书信息记录行的功能。 1 删…

高颜值官网(3):家居用品网站12个,好的创意都在这里。

hello&#xff0c;大家好&#xff0c;我是大千UI工场&#xff0c;本文为大家带来家居用品网站UI&#xff0c;供大家欣赏。

项目代码优化(1)——下单逻辑

给一个电商开发的系统排查&#xff0c;发现漏洞很多。很多经验不够的开发者很容易忽视的逻辑错误陷阱。在给一个项目做二次开发时候&#xff0c;检测到的相关经典案例。这里整理支付和产品相关的逻辑&#xff0c;方便后续查看。&#xff0c;这里进行一些简单的逻辑漏洞梳理与修…

Ubuntu 22.04 LTS 上安装 MySQL8.0.23(在线安装)

目录 在线安装MySQL 步骤1&#xff1a;更新软件包列表 步骤2&#xff1a;安装MySQL服务器 步骤3&#xff1a;启动MySQL服务 步骤4&#xff1a;检查MySQL状态 步骤5&#xff1a;修改密码、权限 在线安装MySQL 步骤1&#xff1a;更新软件包列表 在进行任何软件安装之前&a…

p9函数(1)

int Add(int x,int y) { int z0; zxy; return z; } int main() { int a10; int b20; int sumAdd(a,b); printf("%d\n",sum); return 0; } 字符串求长度 int main() { char arr1[]"bit"; char arr2[20]"###…

移动UI: 什么特征会被认为是简洁风格,用案例告诉你

什么是简洁风格&#xff0c;恐怕一百个人有一百个是理解&#xff0c;本文通过理论分析案例的方式进行探讨。 移动 UI 中的简洁风格通常具有以下几个特征&#xff1a; 1. 平面化设计&#xff1a; 简洁风格的移动 UI 善于运用平面化设计&#xff0c;即去除过多的阴影、渐变和立…

水冷液冷负载系统的六种基本类型

您可以选择六种基本类型的冷却系统&#xff0c;以满足负载的冷却需求。每个人都有其优点和缺点。本文旨在识别不同类型的冷却系统并确定它们的优缺点&#xff0c;以便您可以根据自己的需求做出明智的选择。 液体冷却系统有六种基本类型&#xff1a; 1.液对液 2.闭环干燥系统…

深度讲解 UUID/GUID 的结构、原理以及生成机制

目录 一. 前言 二. 被广泛使用 三. UUID 的结构 3.1. 必须了解的 3.2. 十六进制数字字符&#xff08;hexDigit&#xff09; 3.3. UUID 基本结构 3.4. 类型&#xff08;变体&#xff09;和保留位 3.5. 版本&#xff08;子类型&#xff09; 3.6. 时间戳 3.7. 时钟序列 …

管理《欧盟数字服务法》交易者要求

《数字服务法》合规性 根据《数字服务法》(DSA) 的要求&#xff0c;对于在欧盟地区 (EU) 通过 App Store 分发 App 的所有交易商&#xff0c;Apple 需要验证并显示其联系信息。请指明你是否将以交易商或非交易商的身份在欧盟地区分发任何内容。进一步了解你是否应为交易商。 …

[激光原理与应用-101]:南京科耐激光-激光焊接-焊中检测-智能制程监测系统IPM介绍 - 5 - 3C行业应用 - 电子布局类型

目录 前言&#xff1a; 一、激光在3C行业的应用概述 1.1 概述 1.2 激光焊接在3C-电子行业应用 二、3C电子行业中激光焊接 2.1 纽扣电池 2.2 均温板 2.3 指纹识别器 2.4 摄像头模组 2.5 IC芯片切割 三、3C行业中激光切割 四、激光在3C行业中的其他应用 4.1 涂层去除…

Golang | Leetcode Golang题解之第222题完全二叉树的节点个数

题目&#xff1a; 题解&#xff1a; func countNodes(root *TreeNode) int {if root nil {return 0}level : 0for node : root; node.Left ! nil; node node.Left {level}return sort.Search(1<<(level1), func(k int) bool {if k < 1<<level {return false}…

ubuntu22.04+pytorch2.3安装PyG图神经网络库

ubuntu下安装torch-geometric库&#xff0c;图神经网络 开发环境 ubuntu22.04 conda 24.5.0 python 3.9 pytorch 2.0.1 cuda 11.8 pyg的安装网上教程流传着许多安装方式&#xff0c;这些安装方式主要是&#xff1a;预先安装好pyg的依赖库&#xff0c;这些依赖库需要对应上pyth…

【Dison夏令营 Day 12】如何用 Python 构建数独游戏

通过本综合教程&#xff0c;学习如何使用 Pygame 在 Python 中创建自己的数独游戏。本指南涵盖安装、游戏逻辑、用户界面和计时器功能&#xff0c;是希望创建功能性和可扩展性数独益智游戏的爱好者的理想之选。 数独是一种经典的数字谜题&#xff0c;多年来一直吸引着谜题爱好…