设计模式之组合模式-创建层次化的对象结构

news2024/12/27 14:39:30

目录

  • 概述
    • 概念
    • 主要角色
    • 应用场景
  • 组合模式的实现
    • 类图
    • NS图
    • 基本代码
    • 组合模式的精髓
    • 意外收获(❀❀)
  • 应用示例-公司组织架构管理
    • 需求
    • 结构图
    • 代码
  • 组合模式的优缺点
    • 优点
    • 缺点
  • 总结

概述

概念

    组合模式是一种结构型设计模式,它允许将对象组合成树形结构来表示“部分-整体”的层次关系。组合模式允许客户端统一处理单个对象和组合对象,使得客户端可以将它们视为相同的数据类型。
    在组合模式中,有两种主要类型的对象:叶子对象和容器对象。叶子对象是组合的最小单位,它不包含任何子对象;而容器对象包含叶子对象和/或其他容器对象,从而形成了一个递归的树形结构。

主要角色

  • Component:组合中的对象声明接口,它定义了组合对象的基本行为,包括添加和删除组成部分等操作。

  • Leaf:叶子是组合模式中的基本角色,它表示组合对象中的单个对象,通常是不可变的。叶子通常没有子节点,它们只包含一些基本的属性和行为。

  • Composite:是组合模式中的核心角色,它由多个组件构成,可以包含叶子节点和非叶子节点。组合对象是可变的,它们可以通过添加或删除组成部分来改变自己的状态。组合对象负责管理自己的组成部分,并提供一些特定于组合对象的操作,如添加或删除组成部分等。

    叶子节点和非叶子节点都是组件的一种,它们都实现了 Component 接口。

    在使用组合模式时,客户端需要知道如何处理组合对象和单个对象,以及如何使用抽象组件中定义的接口来访问它们的行为。具体组件和抽象组件则负责实现组合对象和单个对象的具体行为。组合对象则负责管理它包含的组成部分,并提供一些特定于组合对象的操作。

应用场景

  • 文件系统中的文件和文件夹
  • 菜单中的菜单项和菜单
  • 网页中的页面和页面元素

组合模式的实现

    组合模式的实现需要定义一个抽象基类和两个具体的实现类。抽象基类中定义了组合对象和单个对象的共同接口,具体实现类中分别实现了组合对象和单个对象的具体行为。

类图

在这里插入图片描述

NS图

在这里插入图片描述

基本代码

抽象类

abstract class Component {
    protected String name;
    public Component(String name){
        this.name=name;
    }

    public abstract void add(Component component);
    public abstract void remove(Component component);
    public abstract void display(int depth);
}

组合对象


public class Composite extends Component {
    public ArrayList<Component> children = new ArrayList<Component>();


    public Composite(String name) {
        super(name);

    }

    @Override
    public void add(Component component) {
        children.add(component);
    }

    @Override
    public void remove(Component component) {
        children.remove(component);
    }

    @Override
    public void display(int depth) {
        //显示枝结点名称
        for (var i = 0; i < depth; i++) {
            System.out.print("-");
        }
        System.out.println(name);

        //对其下级进行遍历
        for (Component item : children) {
            item.display(depth + 2);
        }

    }
}

叶子节点


public class Leaf extends Component{
    public Leaf(String name){
        super(name);
    }
    //为了消除叶子结点和枝结点的区别,所以他们具备相同的接口,即便叶子结点不需要这两个功能
    @Override
    public void add(Component component) {
        System.out.println("不能添加叶子了");

    }

    @Override
    public void remove(Component component) {
        System.out.println("不能移除叶子");
    }

    @Override
    public void display(int depth) {
//叶子结点的显示方法,只显示其名称和级别
        for(var i=0;i<depth;i++){
            System.out.print("-");}
            System.out.println(name);


    }
}

客户端


public class Client {
    public static void main(String[] args) {
        Composite root=new Composite("root");
        root.add(new Leaf("Leaf A"));
        root.add(new Leaf("Leaf B"));

        Component comp=new Composite("composite X");
        comp.add(new Leaf("Leaf XA"));
        comp.add(new Leaf("Leaf XB"));
        root.add(comp);

        Component comp2=new Composite("composite XY");
        comp2.add(new Leaf("Leaf XYA"));
        comp2.add(new Leaf("Leaf XYB"));
        comp.add(comp2);

        Leaf leaf=new Leaf("leaf C");
        root.add((leaf));

        Leaf leaf2=new Leaf("leaf D");
        root.add((leaf2));
        root.remove(leaf2);

        root.display(1);
    }
}

输出结果
在这里插入图片描述

组合模式的精髓

    在于将单个对象和组合对象统一对待,从而使得客户端可以一视同仁地操作它们,而无需关心其具体类型。这种设计思想体现了面向对象设计中的几个重要原则和模式:

  1. 透明性:组合模式通过将组合对象和叶子对象抽象成统一的组件类,让客户端对它们进行透明处理。客户端不需要知道正在处理的是叶子对象还是组合对象,只需通过统一的接口与它们交互。
  2. 单一职责原则:组合模式将对象的结构和行为分离开来,每个对象专注于自己的职责。叶子对象负责完成具体的操作,而组合对象负责管理子对象。
  3. 易扩展性:当系统需要增加新的叶子对象或组合对象时,组合模式无需修改现有的客户端代码,而是通过扩展组件类来实现。这提高了系统的灵活性和可扩展性。
  4. 清晰的层次结构:组合模式能够清晰地描述对象的层次结构,使得系统的整体结构更加直观和易于理解。

在代码中的体现:

    客户端在创建对象时,无论是Leaf还是Composite,使用相同的构造函数或方法来创建对象,比如new Leaf(“Leaf A”)和new Composite(“composite X”)。

    在添加子对象时,客户端使用相同的方法add来添加子对象,无论是添加Leaf还是添加Composite对象。

    在移除对象时,客户端同样使用相同的方法remove来移除对象,无论是移除Leaf还是移除Composite对象。

    这些操作都体现了客户端代码以统一的方式处理单个对象和对象的组合,而无需关心它们具体是哪一种类型。这种设计使得客户端代码更加简洁、清晰,并且更容易适应系统变化。

    这样的原因除了因为Composite和leaf都继承于Component。也可以从树的角度进行思考,比如下图,当把组合对象以及下面的叶对象看出一个整体,对于根组合对象来说,也可以当成一个叶对象。
在这里插入图片描述

意外收获(❀❀)

    调试代码中,还看到了一个特别有意思的现象:
    创建Composite对象的时候,先走父类构造方法,此时children:null
在这里插入图片描述
    走完父类构造之后,先走了public ArrayList children = new ArrayList();之后children:size=0,再这之后才结束构造方法
在这里插入图片描述
    children的变化说明了什么呢?为什么会这样呢?

    因为在Java中,当一个对象被创建时,成员变量会先被初始化为默认值,对于引用类型来说,默认值是null。这也解释了为什么在调试时会看到children开始是null。

children的变化如下:

    首先,children作为Composite的成员变量被初始化为null。
    然后,会调用Composite父类的构造函数super(name)来初始化父类的属性和状态。
    最后,执行Composite类的构造函数体中的其他代码,在这里手动将children赋值为new ArrayList()来创建一个新的ArrayList对象并赋给children。

    因此,在调试时会看到children开始是null,然后在执行完父类构造函数之后,children才不是null。

最后想说:

    在Java中,类的实例化过程分为以下几个步骤:

1、所有的成员变量(包括实例变量和类变量)都会先被初始化为默认值。对于引用类型来说,默认值是null,对于数值类型来说,默认值是0或0.0,布尔类型的默认值是false。

2、然后会调用相应的构造函数进行初始化,其中可以在构造函数中对成员变量进行进一步赋值或初始化操作

    在调试时看到children开始是null,正是因为它在实例化时被初始化为默认值null。而在执行完父类构造函数后,手动将children赋值为new ArrayList(),从而创建了一个新的ArrayList对象并赋给children。

    再加一条,作为一个带着叶子节点的Composite,是组合好一家子之后才往父节点挂靠的,也就和前面我说的作为一个整体可以看成一个叶子。
具体代码如下:
在这里插入图片描述
示意图:
在这里插入图片描述
    虽然代码中取名children,不要理解成子节点,举个例子,如果Composite是妈妈,children不是孩子,而是子宫,子宫用来添加孩子(叶子),说这个的原因,是因为我被开始的自己的理解蠢哭了
在这里插入图片描述
在这里插入图片描述

应用示例-公司组织架构管理

需求

    使用组合模式管理公司组织架构可以帮助公司更好地组织和管理其内部的部门、员工以及其它相关的业务实体。业务需求层级结构管理:公司内部通常存在多层级的组织结构,包括总公司、分公司、部门、小组等。需要一个灵活的方式来管理这种层级关系,使得可以方便地进行增加、删除、修改各个层级的组织单元。

结构图

在这里插入图片描述

代码

抽象公司类

abstract class Company {
    protected String name;
    public Company(String name ){
        this.name=name;
    }
    public abstract void  add(Company company);
    public abstract void remove(Company company);
    public abstract void display(int dept);
    public abstract void lineOfDuty();//履行职责,不同部门需履行不同的职责
}

组织类


public class ConcreteCompany extends Company {
    protected ArrayList<Company> children = new ArrayList<Company>();

    public ConcreteCompany(String name) {
        super(name);
    }

    @Override
    public void add(Company company) {
        children.add(company);
    }

    @Override
    public void remove(Company company) {
        children.remove(company);
    }

    @Override
    public void display(int dept) {
        for (var i = 0; i < dept; i++)
            System.out.print("-");
        System.out.println(name);
        for (Company item : children) {
            item.display(dept + 2);//为什么+2
        }
    }

    @Override
    public void lineOfDuty() {
        for (Company item : children) {
            item.lineOfDuty();
        }
    }
}

财务部门


public class FinanceDepartment extends Company{
    public FinanceDepartment(String name){
        super(name);
    }
    @Override
    public void add(Company company) {

    }

    @Override
    public void remove(Company company) {

    }

    @Override
    public void display(int dept) {
        for (var i = 0; i <dept ; i++) {
            System.out.print("-");
        }
        System.out.println(name);
    }

    @Override
    public void lineOfDuty() {
        System.out.println(name+"公司财务管理");
    }
}

人事部门


public class HRDepartment extends Company{
    public HRDepartment(String name){
        super(name);
    }
    @Override
    public void add(Company company) {

    }

    @Override
    public void remove(Company company) {

    }

    @Override
    public void display(int dept) {
        for (var i = 0; i <dept ; i++) {
            System.out.print("-");
        }
        System.out.println(name);
    }

    @Override
    public void lineOfDuty() {
        System.out.println(name+"员工招聘培训管理");
    }
}

客户端

public class Client {
    public static void main(String[] args) {
        Company root=new ConcreteCompany("北京总公司");
        root.add(new HRDepartment("总公司人力资源部"));
        root.add(new FinanceDepartment("总公司财务部"));

        Company comp=new ConcreteCompany("西南分公司");
        comp.add(new HRDepartment("西南分公司人力资源部"));
        comp.add(new FinanceDepartment("西南分公司财务部") );
        root.add(comp);

        Company comp2=new ConcreteCompany("成都分公司");
        comp2.add(new HRDepartment("成都分公司人力资源部"));
        comp2.add(new FinanceDepartment("成都分公司财务部") );
        comp.add(comp2);

        Company comp3=new ConcreteCompany("重庆分公司");
        comp3.add(new HRDepartment("重庆分公司人力资源部"));
        comp3.add(new FinanceDepartment("重庆分公司财务部") );
        comp.add(comp3);

        System.out.println("结构图:");
        root.display(1);
        System.out.println("职责");
        root.lineOfDuty();
    }
}

输出结果
在这里插入图片描述

组合模式的优缺点

优点

  • 可以将复杂的层次结构变得简单化
  • 可以统一处理组合对象和单个对象
  • 可以提高代码的复用性和可维护性

缺点

  • 对于大型的层次结构,组合模式可能会导致性能问题
  • 组合模式的实现需要定义多个类,增加了代码量
  • 组合模式的理解和实现需要一定的设计模式知识

总结

    组合模式是一种常用的设计模式,它可以将复杂的层次结构变得简单化,并且可以统一处理组合对象和单个对象。通过组合模式,可以将客户端代码与对象的层次结构分离,客户端只需要面向抽象构件(Component)进行操作,无需关心具体是哪种类型的对象。
    总的来说,组合模式的核心思想是将对象组织成树形结构,并提供统一的接口,使得客户端可以一致地处理单个对象和对象的组合。这种模式在处理具有层次结构的对象时非常有用,如组织架构、文件系统等。

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

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

相关文章

xshell和linux什么关系,其实很简单

如果你是从事网络安全相关的工作人员&#xff0c;那你一定对很多人xshell和linux这两词很熟悉&#xff0c;那么xshell和linux究竟是什么关系呢&#xff1f;今天就让小编给你详细讲讲。 xshell和linux什么关系 一、xshell和linux什么关系 Xsehll是一款在Windows平台上运行的远…

在WSL2中安装多个Ubuntu实例

参考&#xff1a;How to install multiple instances of Ubuntu in WSL2 本文主要内容 第一步&#xff1a;在 WSL2 中安装最新的 Ubuntu第二步&#xff1a;下载适用于 WSL2 的 Ubuntu 压缩包第三步&#xff1a;在 WSL2 中安装第二个 Ubuntu 实例第四步&#xff1a;登录到第二个…

pyspark将数据多次插入表的时候报错

代码 报错信息 py4j.protocol.Py4JJavaError: An error occurred while calling o129.sql. : org.apache.spark.sql.catalyst.parser.ParseException: mismatched input INSERT expecting <EOF>(line 12, pos 0) 原因 插入语句结束后没有加&#xff1b;结尾 把两个&am…

自定义注解+拦截器/AOP切面 实现权限管理

一、通过拦截器实现 1 权限表 为了方便&#xff0c;我直接用的现成的权限表&#xff0c;这是表结构 2 自定义注解 首先&#xff0c;创建一个自定义注解&#xff0c;用于controller层的方法或类上 // Target表示该注解可以用在方法和类上 Target({ElementType.METHOD, Ele…

5.注释和文档

本文讲解 Java 中的注释以及文档 ~ 文章目录 1. 注释1.1 引言1.1.1 何为注释&#xff1f;1.1.2 注释有何用&#xff1f; 1.2 类注释1.2 方法注释 2. 文档 1. 注释 1.1 引言 1.1.1 何为注释&#xff1f; 在我看来&#xff0c;注释无非是对一行或多行代码作的解释罢了。它能让…

Java学习_day08_finalnativeabstract接口

文章目录 final关键字注意 native关键字abstract关键字抽象类定义继承 接口定义实现 final关键字 final关键字表示常量&#xff0c;其值在程序运行期间不会改变。 final用来修饰变量&#xff0c;可以是静态变量&#xff0c;也可以是成员变量&#xff0c;也可以是局部变量&…

连接图书馆wifi无法验证如何解决

我们去图书馆连接wifi&#xff0c;无法验证自己身份&#xff0c;怎么办&#xff1f; 一般是电脑怀疑是不安全&#xff0c;进行了拦截。 我们点击不安全 再点击 与此站点的连接不安全 &#xff0c; 了解详情就可以显示登陆界面了&#xff0c;

点击清空按钮表单数据不能清空

清空选项只能清空角色和状态的数据,无法清空用户名的数据, 经过排查后发现 username这个单词写错了,导致点击清空searchFormRef?.resetFields()不能清空用户名数据

算法之路(一)

&#x1f58a;作者 : D. Star. &#x1f4d8;专栏 :算法小能手 &#x1f606;今日分享 : 如何学习&#xff1f; 在学习的过程中&#xff0c;不仅要知道如何学习&#xff0c;还要知道避免学习的陷阱。1. 睡眠不足&#xff1b;2. 被动学习和重读&#xff1b;3. 强调标记或画线&am…

接口--抽象方法

回答问题&#xff1a; 1.接口是什么&#xff1f; 2.接口中可以包含什么内容&#xff1f; 3.如何定义接口格式&#xff1f; 4.接口定义抽象方法格式&#xff1f; Code //接口是公共规范标准&#xff0c;类似于“模具” //如何定义接口格式&#xff1f;/** public interface 接…

【Qt之QVariant】使用

介绍 QVariant类类似于最常见的Qt数据类型的联合。由于C禁止联合类型包括具有非默认构造函数或析构函数的类型&#xff0c;大多数有趣的Qt类不能在联合中使用。如果没有QVariant&#xff0c;则QObject::property()和数据库操作等将会受到影响。 QVariant对象同时持有一个单一…

View 自定义 - 路径 Path

参考文章 一、概念 用于描述顺序 & 区域&#xff0c;单使用无法产生效果。 图形绘制的本质是先画点再将点连接起来&#xff0c;所以点与点之间是存在一个先后顺序的。图形的方向影响的是&#xff1a;添加图形时确定闭合顺序(各个点的记录顺序)、图形的渲染结果(是判断图形…

ArcGIS进阶:水源涵养功能分级评价操作

首先抛出水源涵养重要性评价的公式&#xff1a;水源涵养量降雨量-蒸散发量-地表径流量&#xff0c;其中地表径流量降雨量*平均地表径流系数 声明&#xff1a;以下数据来源于来自于牛强老师书籍&#xff08;城乡规划GIS技术&#xff09;。 以下给出重要性评价阈值表&#xff1…

『MySQL快速上手』-⑥-表的约束

文章目录 1.空属性2.默认值3.列描述4.zerofill5.主键6.自增长7.唯一键8.外键9.综合案例 真正约束字段的是数据类型&#xff0c;但是数据类型约束很单一&#xff0c;需要有一些额外的约束&#xff0c;更好的保证数据的合法性&#xff0c;从业务逻辑角度保证数据的正确性。 1.空…

高校为什么需要企业数据库?

随着信息化数字化的发展&#xff0c;企业数据库已经成为高校不可或缺的一部分。企业数据库一般整合了多维度企业数据信息。比如&#xff0c;艾思依托丰富的数据沉淀和领先的模型算法&#xff0c;打造“1N”产业大数据平台&#xff0c;包含“1个企业数据中心”一一涵盖全国2.4亿…

计算机提示找不到concrt140.dll怎么办,分享4个有效的修复方法

在计算机使用过程中&#xff0c;我们经常会遇到一些错误提示或者系统崩溃的情况。其中&#xff0c;concrt140.dll是一个常见的错误提示&#xff0c;这个错误通常会导致某些应用程序无法正常运行。为了解决这个问题&#xff0c;我们需要采取一些修复措施。本文将介绍4个修复conc…

基于显著性的无人机多光谱图像语义杂草检测与分类

Saliency-Based Semantic Weeds Detection and Classification Using UAV Multispectral Imaging(2023&#xff09; 摘要1、介绍2、相关工作2.1 监督学习2.2 半监督学习2.3 无监督学习 3、方法3.1 贡献3.2 PC/BC-DIM NEURAL NETWORK&#xff08;预测编码/有偏竞争-分裂输入调制…

20个Python实用小技巧!来自十年老程序员的推荐~

文章目录 1.用itertools排列2.单行条件表达式3. 反转字符串4. 使用 Assert 处理异常5. 对多个输入使用拆分6. 用 zip() 转置矩阵7. 资源上下文管理器8. 下划线作为分隔符9. 尝试 f 字符串格式10.用这个技巧交换整数11. 使用 lambda 代替函数12.多次打印无循环13. 将字符串解包为…

基于机器学习的 ICU 脑血管疾病死亡风险智能预测系统

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长 Wechat / QQ 名片 :) 1. 项目简介 重症患者或重大手术后的患者在重症监护室&#xff08;ICU&#xff09;内通过多种生命支持系统以维持生理功能。患者在ICU 内会被频繁持续的记录生命体征和实验室测量等多种数据。由于高频…

电大搜题——搜索难题

添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff09; 广东开放大学是一所素有口碑的知名学府&#xff0c;一直致力于为广大学员提供优质的教育资源和学习支持。随着科技的不断发展&#xff0c;电子学习成为了现代学习的主要方式之一。为了更好地满足学员的学习…