【设计模式】组合模式(Composite Pattern)

news2024/9/23 1:33:03

组合模式属于结构型模式,又可以叫做部分-整体模式,主要解决客户程序在具有整体和部分的层次结构中,处理一组相似对象比处理单一对象费时费力的问题。例如,一个图形,它可以是一个简单的圆形、方形或一条线(部分),同时它也可以是有圆形、方形和直线组合而成的复杂图形(整体),而实现这些简单图形相比复杂图形肯定一个简单一个复杂。此时客户端程序不干了,既然复杂图形是由多个简单图形构成,其本质都是一样的,那为什么不能像操作简单图形一样操作复杂图形,这就需要使用组合模式了。

文章目录

  • 组合模式的介绍
    • 优点
    • 缺点
    • 应用场景
  • 组合模式的使用
    • 类图
      • 组合模式中存在三个角色
    • 组合模式的封装(透明)实现方法
      • 第一步,编写抽象构件角色
      • 第二步,编写树叶构件角色
      • 第三步,编写树枝构件角色
      • 第四步,编写测试类测试
    • 组合模式的安全实现方法
      • 第一步,修改抽象构件角色
      • 第二步,修改树叶构件角色
      • 第三步,修改树枝构件角色
      • 第四步,修改测试类测试


组合模式的介绍

​ 组合模式通过将一组相似的简单对象按照树形结构组合成一个单一的复杂对象,用以表示“部分-整体”的层次结构,使得客户程序在调用组合对象和单一对象时具有一致性。此时客户程序可以忽略组合对象与单一对象的不同,能够像处理单一对象一样处理组合对象

​ 组合模式规定简单对象和组合对象都需继承一个相同的父类,此时将父类看作根节点,一个个不能拆分的简单对象是叶子节点,而组合对象就是其他子节点。这样就完成了一个清晰的树形组合。

优点

  • 让客户程序忽略层次的差异
  • 客户程序与复杂对象的内部结构解耦
  • 通过递归可以方便的对整个层次结构进行控制
  • 新增一个简单对象和一个组合对象并不用改动此结构中的其他代码,符合开闭原则

缺点

  • 因为子节点继承的是实现类,不是接口,违反了依赖倒置原则

应用场景

  • 在具有整体和部分的层次结构中,希望通过一种方式忽略整体与部分的差异
  • 使用部分-整体的场景,如树形菜单,文件、文件夹的管理
  • Java AWT/Swing 中的简单组件 JTextComponent 有子类 JTextFieldJTextArea,容器组件 Container也有子类 WindowPanel
  • 公司存在多个部门,同时公司下属的分公司中也存在多个部门,而分公司下属的办事处也存在相同部门



组合模式的使用

​ 假设我现在要设计一套UI界面,而UI界面是由多个组件构成的,我现在要准备一些简单的组件:按钮组件、文本组件、输入框组价,以及用这些简单组件组合的登录框组件。

类图

image-20221124211154990

组合模式中存在三个角色

  • 抽象构件(Component)角色:它是一个抽象类,上面实现中的抽象组件类充当这个角色,它给参加组合的对象定义出了公共的接口及默认行为,可以用来管理所有的子对象(在封装下的组合模式中是这样的)。在安全式的组合模式里,构件角色并不定义出管理子对象的方法,这一定义由树枝结构对象给出(也就是登录框组件)。
  • 树叶构件(Leaf)角色:这是一个基本的组件类,是树型结构中的叶子节点,上面实现中的按钮组件、文本域组件和输入框组件充当这个角色,主要实现参加组合的原始对象的基本行为。
  • 树枝构件(Composite)角色:代表参加组合的有下级子对象的对象,上面实现中的登录框组件充当这个角色,树枝对象给出所有管理子对象的方法实现,如添加子组件、删除子组件等等



组合模式的封装(透明)实现方法

​ 在此方式中,由于抽象构件声明所有子类中的全部方法,所以客户程序没必要区分简单组件和组合组件,这对客户程序来说是透明的、看不见的。但缺点是简单组件本身不具备增加、删除子组件等管理方法,但现在必须却要实现它们,这样会带来一些安全性问题。

第一步,编写抽象构件角色

抽象组件

package 设计模式.组件模式;

/**
 * 组件的抽象类,抽象构件角色
 */
public abstract class 抽象组件 {
    public String name; // 组件名称

    public 抽象组件(String name) {
        this.name = name;
    }

    public abstract void 绘制();
    public abstract void 添加(抽象组件 组件);
    public abstract void 删除(抽象组件 组件);
}

第二步,编写树叶构件角色

按钮组件

package 设计模式.组件模式;

/**
 * 简单组件
 */
public class 按钮组件 extends 抽象组件 {

    public 按钮组件(String name) {
        super(name);
    }

    @Override
    public void 绘制() {
        // 重写父类抽象方法
        System.out.println("名为“" + name + "”的【按钮组件】绘制完成");
    }

    @Override
    public void 添加(抽象组件 组件) {
        // 因为简单组件无法添加或移除其他组件,所以简单组件的添加和删除方法没有任何意义
        System.out.println("基础组件不支持添加!");
    }

    @Override
    public void 删除(抽象组件 组件) {
        // 因为简单组件无法添加或移除其他组件,所以简单组件的添加和删除方法没有任何意义
        System.out.println("基础组件不支持删除!");
    }
}

文本域组件

package 设计模式.组件模式;

/**
 * 简单组件
 */
public class 文本域组件 extends 抽象组件 {

    public 文本域组件(String name) {
        super(name);
    }

    @Override
    public void 绘制() {
        // 重写父类抽象方法
        System.out.println("名为“" + name + "”的【文本域组件】绘制完成");
    }

    @Override
    public void 添加(抽象组件 组件) {
        System.out.println("基础组件不支持添加!");
    }

    @Override
    public void 删除(抽象组件 组件) {
        System.out.println("基础组件不支持删除!");
    }
}

输入框组件

package 设计模式.组件模式;

/**
 * 简单组件
 */
public class 输入框组件 extends 抽象组件 {

    public 输入框组件(String name) {
        super(name);
    }

    @Override
    public void 绘制() {
        // 重写父类抽象方法
        System.out.println("名为“" + name + "”的【输入框组件】绘制完成");
    }

    @Override
    public void 添加(抽象组件 组件) {
        System.out.println("基础组件不支持添加!");
    }

    @Override
    public void 删除(抽象组件 组件) {
        System.out.println("基础组件不支持删除!");
    }
}

第三步,编写树枝构件角色

登录框组件

package 设计模式.组件模式;

import java.util.ArrayList;
import java.util.List;

/**
 * 组合组件,由多个简单组件组成
 */
public class 登录框聚合组件 extends 抽象组件 {

    public List<抽象组件> 子组件列表 = new ArrayList<>();

    public 登录框聚合组件(String name) {
        super(name);
    }

    @Override
    public void 绘制() {
        // 编写复制的绘制方法,无需让客户程序(测试类)再编写
        System.out.println("名为" + name + "的【登录框组件】开始绘制:");
        for (抽象组件 子组件 : 子组件列表) {
            System.out.print("\t├---"); // 做一点显示的加工
            子组件.绘制();
        }
        System.out.println(name + "绘制完成!");

    }

    @Override
    public void 添加(抽象组件 组件) {
        // 添加子组件
        子组件列表.add(组件);
    }

    @Override
    public void 删除(抽象组件 组件) {
        // 删除不需要的子组件
        子组件列表.remove(组件);
    }
}

第四步,编写测试类测试

测试类

package 设计模式.组件模式;

public class 测试类 {
    public static void main(String[] args) {
        // 绘制两个按钮
        new 按钮组件("一号按钮").绘制();
        new 按钮组件("二号按钮").绘制();

        // 绘制一个输入框和文本域
        System.out.println();
        new 输入框组件("一号输入框").绘制();
        new 文本域组件("测试文本").绘制();

        // 绘制一个登陆框
        System.out.println();
        // 在透明方式下,客户程序无序知道是简单组件还是组合组件,直接用抽象组件接收就行
        抽象组件 条令条例文本域 = new 文本域组件("条令条例文本域");
        抽象组件 登录框 = new 登录框聚合组件("一号登录框");
        登录框.添加(new 按钮组件("登陆按钮"));
        登录框.添加(new 按钮组件("忘记密码按钮"));
        登录框.添加(new 输入框组件("账号输入框"));
        登录框.添加(new 输入框组件("密码输入框"));
        登录框.添加(条令条例文本域);
        // 从登录框中删除了文本组件
        登录框.删除(条令条例文本域);
        登录框.绘制();
    }
}

测试结果

名为“一号按钮”的【按钮组件】绘制完成
名为“二号按钮”的【按钮组件】绘制完成

名为“一号输入框”的【输入框组件】绘制完成
名为“测试文本”的【文本域组件】绘制完成

名为一号登录框的【登录框组件】开始绘制:
	├---名为“登陆按钮”的【按钮组件】绘制完成
	├---名为“忘记密码按钮”的【按钮组件】绘制完成
	├---名为“账号输入框”的【输入框组件】绘制完成
	├---名为“密码输入框”的【输入框组件】绘制完成
一号登录框绘制完成!

Process finished with exit code 0

image-20221124212855012




组合模式的安全实现方法

​ 在安全方式中,抽象构件将管理子构件的方法移到树枝构件中,抽象构件和树叶构件不再持有对子对象的管理方法,这样就避免了上一种方式的安全性问题,但由于叶子和分支有不同的接口,客户程序在调用时就必须要知道树叶对象和树枝对象的存在,这样也就失去了透明性。

第一步,修改抽象构件角色

仅仅去掉了无用的添加删除方法

抽象组件

package 设计模式.组件模式;

/**
 * 组件的抽象类,抽象构件角色
 */
public abstract class 抽象组件 {
    public String name; // 组件名称

    public 抽象组件(String name) {
        this.name = name;
    }

    public abstract void 绘制();
}

第二步,修改树叶构件角色

仅仅去掉了无用的添加删除方法

按钮组件

package 设计模式.组件模式;

/**
 * 简单组件
 */
public class 按钮组件 extends 抽象组件 {

    public 按钮组件(String name) {
        super(name);
    }

    @Override
    public void 绘制() {
        // 重写父类抽象方法
        System.out.println("名为“" + name + "”的【按钮组件】绘制完成");
    }
}

文本域组件

package 设计模式.组件模式;

/**
 * 简单组件
 */
public class 文本域组件 extends 抽象组件 {

    public 文本域组件(String name) {
        super(name);
    }

    @Override
    public void 绘制() {
        // 重写父类抽象方法
        System.out.println("名为“" + name + "”的【文本域组件】绘制完成");
    }
}

输入框组件

package 设计模式.组件模式;

/**
 * 简单组件
 */
public class 输入框组件 extends 抽象组件 {

    public 输入框组件(String name) {
        super(name);
    }

    @Override
    public void 绘制() {
        // 重写父类抽象方法
        System.out.println("名为“" + name + "”的【输入框组件】绘制完成");
    }
}

第三步,修改树枝构件角色

登录框组件

无需变化,和上面的一样,只需要去掉@Override注解


第四步,修改测试类测试

仅仅修改两行代码:

文本域组件 条令条例文本域 = new 文本域组件("条令条例文本域");
登录框聚合组件 登录框 = new 登录框聚合组件("一号登录框");

测试类

package 设计模式.组件模式;

public class 测试类 {
    public static void main(String[] args) {
        new 按钮组件("一号按钮").绘制();
        new 按钮组件("二号按钮").绘制();

        // 绘制一个输入框和文本域
        System.out.println();
        new 输入框组件("一号输入框").绘制();
        new 文本域组件("测试文本").绘制();

        // 绘制一个登陆框
        System.out.println();
        // 在安全方式下,客户程序需要知道一个组件是简单组件还是组合组件
        文本域组件 条令条例文本域 = new 文本域组件("条令条例文本域");
        登录框聚合组件 登录框 = new 登录框聚合组件("一号登录框");
        登录框.添加(new 按钮组件("登陆按钮"));
        登录框.添加(new 按钮组件("忘记密码按钮"));
        登录框.添加(new 输入框组件("账号输入框"));
        登录框.添加(new 输入框组件("密码输入框"));
        登录框.添加(条令条例文本域);
        // 从登录框中删除了文本组件
        登录框.删除(条令条例文本域);
        登录框.绘制();
    }
}

测试结果

和之前的实现方法中的一样

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

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

相关文章

paddleocr检测模型训练记录

标注好数据集后 分为训练集、测试集 数据集格式需要与配置文件一致&#xff0c;为了方便&#xff0c;我直接使用以下格式。 PaddleOCR主目录下&#xff0c;自己新建文件夹&#xff1a;car_plate_images/images_det train、test、里面是图片 det_label_test、det_label_train、…

Python遥感开发之GDAL读写遥感影像

Python遥感开发之GDAL读写遥感影像1 读取tif信息方法一2 读取tif信息方法二3 自己封装读取tif的方法&#xff08;推荐&#xff09;4 对读取的tif数据进行简单运算5 写出tif影像(推荐)前言&#xff1a;主要介绍了使用GDAL读写遥感影像数据的操作&#xff0c;包括读取行、列、投影…

基于51单片机霍尔汽车自行车码表测速测里程显示proteus仿真原理图PCB

功能&#xff1a; 0.本系统采用STC89C52作为单片机 1.LCD1602液晶分三种显示模式 a)显示实时速度和本次里程 b)显示当前时间 c)显示报警速度和总里程 2.超过报警速度将声光报警 3.功能按键介绍 a显示状态下: 上’键——电机速度1 下’键——电机速度-1 设置’键——电机启动/暂…

四、【基础】组件实例三大核心属性之一 state

文章目录1、CODE2、Result2.1、初始化2.2、触发更新3、state简写理解&#xff1a; state是组件对象最重要的属性, 值是对象(可以包含多个key-value的组合)组件被称为"状态机", 通过更新组件的state来更新对应的页面显示(重新渲染组件) 注意&#xff1a; 组件中rende…

算法导论习题—摊还时间代价分析、栈实现队列、贪心算法近似比、集合覆盖问题

在执行的nnn个操作中&#xff0c;有至多⌈lgn⌉⌈lg n⌉⌈lgn⌉个操作的次序是222的幂&#xff0c;这些操作的次序&#xff08;即代价&#xff09;如下 1,2,4,8,⋅⋅⋅,2⌈lgn⌉1, 2, 4, 8, , 2 ⌈lg n⌉ 1,2,4,8,⋅⋅⋅,2⌈lgn⌉ nnn个操作的总代价为 T∑k0⌈lgn⌉2k(n−⌈…

Android App网络通信中利用okhttp实现下拉刷新和上拉加载实战(抓取文章信息 超详细 附源码)

需要源码和工具类请点赞关注收藏后评论区留言私信~~~ 一、实现下拉刷新和上拉加载功能 网络上的信息很多&#xff0c;往往无法依次拉下来&#xff0c;故而App引入了分页加载功能&#xff0c;最开始先展示第一页内容&#xff0c;等到用户拉到该页底部后再去加载下一页内容&…

网络安全面试、实习、校招经验打包分享

整理收集了近些年的资料 内几乎覆盖了各大公司&#xff0c;大厂小厂都有 很多培训机构都是收费提供 本公众号无任何套路全部免费 提供下载学习 为了防止失效&#xff0c;建议下载收藏起来 以后总会用得上&#xff01; 下面截取部分资料 腾讯-安全技术实习生 时长&am…

流媒体技术基础-摄像头接口与标准

一、摄像头接口概括 摄像头按接口分类如下 主板直接接的专用接口 SPI接口&#xff1a;串行传输、速度慢。常用用于MCU DVP接口&#xff1a;并口传输&#xff0c;速度较慢&#xff0c;传输的带宽低。 MIPI接口&#xff1a;手机平台标准接口&#xff0c;差分串口传输&#xff…

CSS3基础

CSS 层叠样式表Cascading Style Sheets&#xff0c;缩写为CSS&#xff0c;是一种样式表语言&#xff0c;用来描述HTML或XML&#xff08;包括如SVG、MathML、XHTML 之类的XML 分支语言&#xff09;文档的呈现。 CSS描述了在屏幕、纸质、音频等其它媒体上的元素应该如何被渲染的问…

AI 预测世界杯比赛结果,惊掉下巴

哈喽&#xff0c;大家好。 今天看到Kaggle上有一个预测世界杯比赛结果的项目&#xff0c;截至目前 4 场比赛预测结果全中。 今天把源码研究了一下&#xff0c;做了中文注释&#xff0c;给大家分享下。 文章目录技术提升1. 获取数据集2. 特征工程3. 建模4. 预测技术提升 本文…

“软硬兼施”,全方位守护企业数据安全

编者按&#xff1a;知识管理涉及大量企业数据&#xff0c;所以其数据安全问题不容忽视。如何做好企业知识管理安全&#xff1f;本文分析了企业数据安全问题主要来自哪里&#xff0c;并从设备、部署、人员管控三方面出发&#xff0c;最终指出了解决方案。 关键词&#xff1a;私…

ORB-SLAM3算法学习—Frame构造—基于SAD滑窗的双目特征匹配

文章目录0总述1双目匹配1.1为左目每个特征点建立带状区域搜索表&#xff0c;限定搜索区域。&#xff08;已提前极线校正&#xff09;1.2对左目相机每个特征点&#xff0c;通过描述子在右目带状搜索区域找到匹配点1.3通过SAD滑窗得到匹配修正量bestincR1.4 做抛物线拟合找谷底得…

力扣刷题(代码回忆录)——回溯算法

关于回溯算法&#xff0c;你该了解这些&#xff01;回溯算法&#xff1a;组合问题回溯算法&#xff1a;组合问题再剪剪枝回溯算法&#xff1a;求组合总和&#xff01;回溯算法&#xff1a;电话号码的字母组合本周小结&#xff01;&#xff08;回溯算法系列一&#xff09;回溯算…

变压器励磁电感以及漏感

1 励磁电感(magnetic inductance):脉冲变压器的初级电感 仅在变压器中才出现的名词,也就是一个等效电感值,事实上这个电感是变压器的初级侧电感,作用在其上的电流不会传导到次级,它的作用是拿来对铁芯产生激磁作用,使铁芯内的铁磁分子可以用来导磁,就好比铁芯是磁中性,绕上…

SuperMap iManager for K8S使用XFS文件系统类型出现节点异常解决办法

前段时间&#xff0c;遇到了多个用户在使用iManager for K8S的过程出现K8S节点宕机&#xff0c;或者是使用无法使用任何命令的情况。通过了解之后发现这些客户都存在一个共同点&#xff0c;服务节点的文件系统类型都是XFS&#xff0c;并且使用的NFS作为存储。本篇文章将讲解如何…

窗口函数简介与总结

目录 什么是窗口函数 窗口函数的实现原理 窗口函数使用场景 常用的窗口函数有&#xff1a; 1. 窗口排序函数&#xff1a;ROW_NUMBER()、RANK()、DENSE_RANK()&#xff1b; 2. 窗口聚合函数&#xff1a;SUM()、MIN()、MAX()、AVG()&#xff1b; 3. LAG() 4. LEAD() 5. …

88.Django中间件的说明与使用方法

1. 概述 ​ AOP&#xff08;Aspect Oriented Programming &#xff09;&#xff0c;面向切面编程&#xff0c;是对业务逻辑的各个部分进行隔离&#xff0c;从而使得业务逻辑各部分之间的耦合度降低&#xff0c;提高程序的可重用性&#xff0c;同时提高了开发的效率。可以实现在…

css-实现卡牌的发牌和翻牌动画

场景描述&#xff1a; 打开抽卡界面&#xff0c;卡牌出现并发牌至固定的位置&#xff0c;此时展示的是卡牌的背面&#xff1b;用户点击卡牌时&#xff0c;卡牌进行翻转&#xff0c;并展示卡牌内容&#xff0c;或者发牌后自动进行翻转和展示。 本实例在页面挂载后自动播放动画&…

前端网站动态主题色解决方案

动态主题色替换分两种&#xff1a;UI 组件库主题色替换和系统主题色替换。 组件库 UI 动态主题替换现阶段只在 Element-UI 和 Vant-UI 测试过&#xff0c;根据排查这种方案应该适用于所有类似的动态主题色替换场景。 1. UI 组件库主题色替换 在进入到这一部分之前&#xff0c;…

力扣232 - 用栈实现队列【C语言实现】

用栈实现队列~一、题目描述二、思路分析三、代码详解1、结构声明与展开剖析2、入队【入栈思想】3、获取队头【出栈思想】4、出队【复用思想】5、逐步算法图解四、整体代码展示&#x1f4bb;C语言代码实现五、总结与提炼一、题目描述 示例 1&#xff1a; 输入&#xff1a; [“My…