组合模式-树形结构的处理

 A公司需要筛选出年龄35岁及以上(如果是领导,年龄为45岁及以上)的人。其组织架构图如下。

图 A公司部分组织架构图

图 传统解决方案

public class Development {

    private String name;

    public Development(String name) {
        this.name = name;
    }

    List<Employee> employeeList = new ArrayList<>();
    List<Development> developmentList = new ArrayList<>();

    public void find() {
        for (Employee employee : employeeList) {
            employee.find();
        }
        for (Development development : developmentList) {
            development.find();
        }
    }

    public String getName() {
        return name;
    }
}

public class Employee {

    private Development development;

    private String name;
    private Integer age;
    private Boolean isLead;

    public Employee(Development development,String name, Integer age, Boolean isLead) {
        this.development = development;
        this.name = name;
        this.age = age;
        this.isLead = isLead;
    }

    public void find() {
        if ((!isLead && age >= 35) || (isLead && age >= 45)) {
            System.out.println(development.getName() + ":" + name + "超龄:" + age + " " + (isLead ? "是" : "不是") + "领导");
        }
    }

}

public class Client {

    Development development = new Development("董事会");

    {
        development.employeeList.add(new Employee(development,"黄总", 53,true));

        development.developmentList.add(new Development("行政部"));
        development.developmentList.add(new Development("营销部"));
        Development techDev = new Development("技术部门");
        development.developmentList.add(techDev);

        techDev.employeeList.add(new Employee(techDev,"技术部经理",36,true));
        techDev.developmentList.add(new Development("技术A组"));
        techDev.developmentList.add(new Development("技术C组"));
        Development bDev = new Development("技术B组");
        techDev.developmentList.add(bDev);

        bDev.employeeList.add(new Employee(bDev,"小黄",27,false));
        bDev.employeeList.add(new Employee(bDev,"小张",27,false));
        bDev.employeeList.add(new Employee(bDev,"老刘",35,false));
    }

    public static void main(String[] args) {
        Client client = new Client();
        client.development.find();
//        运行结果:
//        董事会:黄总超龄:53 是领导
//        技术B组:老刘超龄:35 不是领导
    }

}

在上图的架构图中,同一层存在员工及部门两类实体,它们都有个查找的方法。在代码中在成员变量中将这两类分别存储在两个不同容器中,在查找方法中,也分别对这两个类别调用了查找方法。代码有些冗余。

我们可以把同一层的员工及部门看作是同一类,这样我们只需要一个容器及查找方法也是只要在一处调用该方法了。这就是组合模式。

1 组合模式概述

由于容器对象和叶子对象在功能上的区别,在使用这些对象的代码中,必须有区别地对待容器对象和叶子对象,而实际上大多数情况下希望一致地处理它们,因为区别对待这些对象将会使得程序变得复杂。

组合模式为解决此类问题而诞生,可以让叶子对象和容器对象的使用具有一致性。

组合多个对象形成树形结构以表示具有”部分—整体“关系的层次结构。组合模式对单个对象(即叶子对象)和组合对象(即容器对象)的使用具有一致性,有被称为”部分—整体“模式(Part-Whole)。

图 组合模式结构图

Component:抽象构件,可以是接口或抽象类。可以包含所有子类共同行为的声名和实现。定义了访问及管理它的子构建的方法,例如增加、删除、获取子构件。

Leaf: 叶子构件,没有子节点,对于那些访问及管理子构件的方法,可以通过捕获异常等方式进行处理。

Composite: 容器构件,表示容器对象,包含了子节点。子节点可以是叶子节点,也可以是容器节点。

public interface Component {

    void find(); //查找超龄员工

    void addChildren(Component component);

    void removeChildren(Component component);

    Component getChildren(int pos);

}

public class Development implements Component{

    private String name;

    public Development(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    private final List<Component> componentList = new ArrayList<>();

    @Override
    public void find() {
        for (Component component : componentList) component.find();;
    }

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

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

    @Override
    public Component getChildren(int pos) {
        return componentList.get(pos);
    }
}

public class Employee implements Component{

    private String name;
    private Integer age;
    private Boolean isLead;

    public Employee(String name, Integer age, Boolean isLead) {
        this.name = name;
        this.age = age;
        this.isLead = isLead;
    }

    @Override
    public void find() {
        if ((!isLead && age >= 35) || (isLead && age >= 45)) {
            System.out.println(name + "超龄:" + age + " " + (isLead ? "是" : "不是") + "领导");
        }
    }

    @Override
    public void addChildren(Component component) {
        throw new RuntimeException("该方法不可用");
    }

    @Override
    public void removeChildren(Component component) {
        throw new RuntimeException("该方法不可用");
    }

    @Override
    public Component getChildren(int pos) {
        throw new RuntimeException("该方法不可用");
    }

}

public class Client {

    private static final Component component = new Development("董事会");

    static {
        component.addChildren(new Employee("黄总", 53,true));

        component.addChildren(new Development("行政部"));
        component.addChildren(new Development("营销部"));
        Component techCom = new Development("技术部");
        component.addChildren(techCom);

        techCom.addChildren(new Employee("技术部经理",36,true));
        techCom.addChildren(new Development("技术A组"));
        techCom.addChildren(new Development("技术C组"));
        Component bDev = new Development("技术B组");
        techCom.addChildren(bDev);

        bDev.addChildren(new Employee("小黄",27,false));
        bDev.addChildren(new Employee("小张",27,false));
        bDev.addChildren(new Employee("老刘",35,false));
    }

    public static void main(String[] args) {
        component.find();
    }
}

1.1 透明组合模式

抽象构件中声明了所有用于管理成员对象的方法,包括addChildren()、removeChildren等。

优点:确保所有构件类都有相同的接口。在客户端看来,叶子对象与容器所提供的方法是一致的,客户端可以相同地对待所有对象。

缺点:不够安全,叶子对象和容器对象在本质上是有区别的,叶子对象不可能有下一层级的对象,即提供管理成员对象的方法是没有任何意义的。如果在运行阶段调用了这些方法,可能会出错。

透明组合模式是组合模式标准形式。

1.2 安全组合模式

抽象构件中没有声明任何用于管理成员对象的方法,而是在容器构件Composite类中声明并实现了这些方法。

优点:安全,客户端不可能会调用到叶子阶段的管理成员对象的方法。

缺点:不够透明,客户端不能完全针对抽象编程,必须有区别地对待叶子构件和容器构件。

2 优缺点

优点:

  1. 可以清楚地定义分层次的复杂对象,表示对象全部或部分层次,让客户端忽略了层次的差异,方便对整个层次结构进行控制。
  2. 增加新的容器构件和叶子构件都很方便,无须对现有类库进行任何修改,符合开闭原则。

缺点:

  1. 在增加新构件时很难对容器中的构件类型进行限制。当希望一个容器只能有某些特定类型对象时,因为它们都来自相同的抽象层,在这种情况下,必须通过在运行时进行类型检查来实现,这个过程较为复杂。

3 适用场景

  1. 在具有整体和部分的层次结构中,希望通过一种方式忽略整体与部分的差异,客户端可以一致性对待它们。
  2. 一个系统中能够分离出叶子对象和容器对象,而且它们的类型不固定,将来需要增加一些新的类型。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

需求分析案例:消息配置中心

本文介绍了一个很常见的消息推送需求&#xff0c;在系统需要短信、微信、邮件之类的消息推送时&#xff0c;边界如何划分和如何设计技术方案。 1、需求 一个系统&#xff0c;一般会区分多个业务模块&#xff0c;并拆分成不同的业务系统&#xff0c;例如一个商城的架构如下&am…

flutter(01) windows桌面版 编译环境安装指南

1 flutter环境安装 flutter官网参考&#xff1a;Install | Flutter 先下载flutter SDK>&#xff1a;flutter sdk下载--官网&#xff0c;之后解压到C:\Users\XXX\data&#xff08;这里以该路径为例&#xff0c;但可以为其他自定义路径&#xff09;目录下&#xff0c;在这里…

栈和队列模拟实现(C++)

文章目录 0.码云完整代码1.deque的认识1.1介绍1.2图析1.3性能比较 2.stack的学习2.1模拟实现2.2测试函数 3.queue的学习3.1模拟实现3.2测试函数 4.优先级队列的学习4.0仿函数的引入4.1介绍4.2例题4.3模拟实现 5.测试函数 0.码云完整代码 点击 栈 队列 优先级队列 跳转码云获取…

鸿鹄协助管理华为云与炎凰Ichiban

炎凰对华为云的需求 在炎凰日常的开发中&#xff0c;对于服务器上的需求&#xff0c;我们基本都是采用云服务。目前我们主要选择的是华为云&#xff0c;华为云的云主机比较稳定&#xff0c;提供的云主机配置也比较多样&#xff0c;非常适合对于不同场景硬件配置的需求&#xff…

石子合并(区间dp模板)

题目描述&#xff1a; dp分析&#xff1a; 解题代码&#xff1a; #include<iostream> using namespace std;const int N1e36;int f[N][N]; int a[N]; int s[N];int main(){int n;cin>>n;for(int i1;i<n;i){scanf("%d",&s[i]);s[i]s[i-1];//前缀和…

2.1数据结构——线性表

一、定义 线性表是具有相同数据类型的n&#xff08;n>0&#xff09;个数据元素的有限序列&#xff0c;&#xff08;n表示表长&#xff0c;n0为空表&#xff09; 用L表示&#xff1a; 位序&#xff1a;线性表中的“第i个” a1是表头元素&#xff1b;an是表尾元素 除第一个…

《吐血整理》进阶系列教程-拿捏Fiddler抓包教程(8)-Fiddler如何设置捕获会话

1.简介 前边几篇宏哥介绍了Fiddler界面内容以及作用。今天宏哥就讲解和分享如何设置Fiddler后&#xff0c;我们就可以捕获会话&#xff0c;进行抓包了。 2.捕获会话的设备 常见的捕获会话的设备分为PC&#xff08;电脑&#xff09;端和手机&#xff08;Android和IOS苹果&…

【SpringⅢ】Spring 的生命周期

目录 &#x1f96a;1 Bean 的作用域 &#x1f969;1.1 singleton&#xff1a;单例模式 &#x1f359;1.2 prototype&#xff1a;原型模式 &#x1f371;1.3 Bean 的其他作用域 &#x1f35c;2 Spring 生命周期(执行流程) &#x1f958;2.1 启动容器 &#x1f372; 2.2 读…

Elasticsearch:使用 ELSER 释放语义搜索的力量:Elastic Learned Sparse EncoderR

问题陈述 在信息过载的时代&#xff0c;根据上下文含义和用户意图而不是精确的关键字匹配来查找相关搜索结果已成为一项重大挑战。 传统的搜索引擎通常无法理解用户查询的语义上下文&#xff0c;从而导致相关性较低的结果。 解决方案&#xff1a;ELSER Elastic 通过其检索模型…

vue elementui table去掉滚动条与实现表格自动滚动且无滚动条

当table内容列过多时&#xff0c;可通过height属性设置table高度以固定table高度、固定表头&#xff0c;使table内容可以滚动。 现在需求是右侧滚动条不好看&#xff0c;需要去除滚动条&#xff0c;并隐藏滚动条所占列的位置。让他可以滚动但是不出现滚动条,不然即时隐藏了滚动…

Mybatis学习笔记

Mybatis 文章目录 Mybatis搭建环境创建Maven工程将数据库中的表转换为对应的实体类配置文件核心配置文件mybatis-config.xml创建Mapper接口映射文件xxxMapper.xmllog4j日志功能 Mybatis操纵数据库示例及要点说明获取参数的两种方式${}#{} 各种类型的参数处理单个字面量参数多个…

keil官网下载MDK的STM32芯片pack包

背景 最近重装了电脑系统&#xff0c;重新安装了MDK所以导致MDK芯片包需要重新下载&#xff0c;软件内下载又太慢&#xff0c;所以趁现在找到了官网下载方法把方法分享出来供大家参考。 1、在浏览器中输入网址&#xff1a;www.keil.arm.com进入如下界面&#xff0c;然后点击&am…

Mock-MOCO使用过程

一、jar包下载&#xff1a;https://github.com/dreamhead/moco 二、准备mock的json文件 data.json内容&#xff1a; ####GET请求 [{"description": "response使用Content-Type为charsetGBK编码格式来查看返回信息为中文的内容","request": {&q…

Tensorflow预训练模型ckpt与pb两种文件类型的介绍

我们在 Tensorflow无人车使用移动端的SSD(单发多框检测)来识别物体及Graph的认识 熟悉了Graph计算图以及在 Tensorflow2.0中function(是1.0版本的Graph的推荐替代)的相关知识介绍 这个tf.function的用法&#xff0c;了解到控制流与计算图的各自作用&#xff0c;无论使用哪种方…

Linux基本指令操作

登陆指令&#xff08;云服务器版&#xff09; 当我们获取公网IP地址后&#xff0c;我们就可以打开xshell。 此时会有这样的界面&#xff0c;我们若是想的登陆&#xff0c;则需要输入以下的指令 ssh 用户名公网IP地址 然后会跳出以下的窗口 接着输入密码——密码便是先前定好…

利用小波包对一维信号进行降噪或压缩(MATLAB)

function [ output_args ] example4_12( input_args ) %EXAMPLE4_12 Summary of this function goes here % Detailed explanation goes here clc; clear; % 设置信噪比和随机数的初始值 snr 3; init 2055615866; % 生成一个原始信号xref和含高斯白噪声的信号x [xref,x] …

微服务契约测试框架-Pact

契约测试 契约测试的思想就是将原本的 Consumer 与 Provider 间同步的集成测试&#xff0c;通过契约进行解耦&#xff0c;变成 Consumer 与 Provider 端两个各自独立的、异步的单元测试。 契约测试的优点&#xff1a; 契约测试与单元测试以及其它测试之间没有重复&#xff0c…

零的奇幻漂移:解密数组中的神秘消失与重生

本篇博客会讲解力扣“283. 移动零”的解题思路&#xff0c;这是题目链接。 思路1 这道题目很有意思。虽然是简单题&#xff0c;其蕴含的玄机还是很多的。正常来讲&#xff0c;这种题目一般都会原地操作&#xff08;不开辟额外的数组&#xff0c;空间复杂度是O(1)&#xff09;&…

计算机组成原理(2)- 浮点数的存储

1、浮点数的表示方法 假设有以下小数&#xff0c;它表示的十进制数是多少呢&#xff1f; 00000000 00000000 00000000 1010.10101*2^3 1*2^1 1*2^-1 1*2^-3 10.625 1010.1010可以用科学计数法来表示为1.0101010 * 2^3。关于科学计数法再举个例子0.10101用科学计数法表示…

uni-app:模态框的实现(弹窗实现)

效果图 代码 标签 <template><view><!-- 按钮用于触发模态框的显示 --><button click"showModal true">显示模态框</button><!-- 模态框组件 --><view class"modal" v-if"showModal"><view cla…