15.一种坍缩式的简单——组合模式详解

当曾经的孩子们慢慢步入社会才知道,那年味渐淡的春节就像是疾驰在人生路上的暂停键。
它允许你在隆隆的鞭炮声中静下心来,瞻前顾后,怅然若失。
也允许你在寂静的街道上屏气凝神,倾听自己胸腔里的那团人声鼎沸。
孩子们会明白的,就像他们步入大学校园时候渐渐明白家乡只有冬夏,再无春秋一样。
人生这场旅途,就是无数个后知后觉的组合。提前看到一些东西不会让我们清醒半分,相反,这一切反而容易让人愈发的沉溺于那来势汹汹的纸醉金迷…
在这里插入图片描述

正月初九,对于多数打工人来说是真正意义上新的一年的开始,就像是一条条沉睡的金鱼被人从湖底捞起再次丢进那个金碧辉煌的鱼缸,开始在整个体系内扮演不同的角色。组织架构、行政管理、经理、开发、部署、运维、测试…
今天,我们不妨就着组织架构这个话题,来拆解下结构型设计模式中将聚合思路用到极致的实现方案:组合模式。


一言

组合模式通过创建对象组的的树形结构,让客户以一致的方式处理个别对象以及组合对象。


当组织开始优化

在这里插入图片描述

概念和思路是为需求服务的,就比如:

我创办了一家名为“Wayne实业”的集团公司,为了集团战略的更好落实,我对组织结构进行了优化。集团公司下设多个省级分公司,每个省级分公司下设地市级办事处。
在这里插入图片描述

现在我要求你在集团公司网页上级联的展示架构信息,有什么思路?


探路式思考

首先明确下,我们不是在具体讨论用Vue或React等成熟前端框架的某个组件更优,而是从实体设计的角度去思考。
相信很多朋友最先想到的就是继承关系,分公司作为集团公司的子类,办事处作为分公司的子类。诚然,需求的描述实在是太契合继承关系的特征了。
在这里插入图片描述
但是我们仔细分析下需求就会发现,这种设计方式实际上存在很大的问题。集团公司有多个分公司,每个分公司下又有大量的办事处。这种设计方式非常不利于对分公司、办事处的管理。可以思考下,一旦需要做遍历或增删操作要投入多大的成本?


组合模式的思考

其实,组合模式非常善于解决这样的问题。当我们要处理的对象可以凭借需求进化成一棵树的时候,这一切反而变得简单。
相信很多朋友读到这里会有疑问,树结构在编程中其实并不是一个容易处理的结构,为什么还说它简单?
这是因为在组合模式下,我们对树上的节点或者叶子的操作都是扁平的,根本不用考虑它是节点还是叶子。
就好像一个立体的三维空间突然坍缩成一个平面,我们需要关注的东西突然少了一个维度,简单的幸福由此蔓延开来。


设计

在这里插入图片描述


代码实现

核心

public abstract class OrganizationComponent {private String name ;private String des;protected void add(OrganizationComponent organizationComponent){throw new UnsupportedOperationException();}protected void remove(OrganizationComponent organizationComponent){throw new UnsupportedOperationException();}protected abstract void print();public OrganizationComponent(String name, String des) {this.name = name;this.des = des;}//setter&getter
}

集团公司

public class WayneIndustries extends OrganizationComponent{List<OrganizationComponent> organizationComponents = new ArrayList<>();public WayneIndustries(String name, String des) {super(name, des);}@Overrideprotected void add(OrganizationComponent organizationComponent) {organizationComponents.add(organizationComponent);}@Overrideprotected void remove(OrganizationComponent organizationComponent) {organizationComponents.remove(organizationComponent);}@Overridepublic String getName() {return super.getName();}@Overridepublic String getDes() {return super.getDes();}@Overrideprotected void print() {System.out.println("--------------------"+getName()+"----------------------");for (OrganizationComponent ogc : organizationComponents)ogc.print();}
}

省公司

public class ProvincialCompany extends OrganizationComponent{List<OrganizationComponent> organizationComponents = new ArrayList<>();public ProvincialCompany(String name, String des) {super(name, des);}@Overrideprotected void add(OrganizationComponent organizationComponent) {organizationComponents.add(organizationComponent);}@Overrideprotected void remove(OrganizationComponent organizationComponent) {organizationComponents.remove(organizationComponent);}@Overridepublic String getName() {return super.getName();}@Overridepublic String getDes() {return super.getDes();}@Overrideprotected void print() {System.out.println("--------------------"+getName()+"----------------------");for (OrganizationComponent ogc : organizationComponents)ogc.print();}
}

办事处

public class Office extends OrganizationComponent{public Office(String name, String des) {super(name, des);}@Overridepublic String getName() {return super.getName();}@Overridepublic String getDes() {return super.getDes();}@Overrideprotected void print() {System.out.println(getName());}
}

客户端

public class Client {public static void main(String[] args) {OrganizationComponent wayne = new WayneIndustries("Wayne实业", "世界500强");OrganizationComponent company1 = new ProvincialCompany("江苏分公司", "综合贸易");OrganizationComponent company2 = new ProvincialCompany("广东分公司", "对外贸易");company1.add(new Office("徐州办事处", "主营冶金"));company1.add(new Office("连云港办事处", "海港事宜"));company1.add(new Office("镇江办事处", "旅游业"));company2.add(new Office("深圳办事处","对外贸易"));company2.add(new Office("珠海办事处","对澳贸易"));wayne.add(company1);wayne.add(company2);wayne.print();
//        company1.print();
//        company2.print();}
}

测试

在这里插入图片描述


组合模式在JDK源码中的应用

笔者在学习设计模式的过程中,有一个很直观的感受就是:设计模式给了普通开发者另一个视角来审视问题,也使得我们看待某些问题变得更灵活。比如顶层抽象,它可以依托于接口也可以依托于抽象类,再比如泛化关系,它可以依托于继承和实现,也可以依托于静态内部类。
这种思维方式的提升就像一个兢兢业业在工地上码砖的工人突然间抬头打量起整个房间的布局甚至整栋大厦的结构原理,那种豁然开朗的感觉或许只有亲历者才能感同身受。

在JDK源码中,组合模式也十分常见。比如我们熟知的HashMap:

		Map<Integer,String> hashMap = new HashMap<>();hashMap.put(0,"江苏");Map<Integer,String> map = new HashMap<>();map.put(1,"浙江");map.put(2,"广东");hashMap.putAll(map);System.out.println(hashMap);

在HashMap中,既可以用put加入一个键值对,也可以用putAll加入多个键值对。
在这里插入图片描述

我们通过审视HashMap的结构可以发现它为了扩展性不但继承了抽象AbstractMap的同时也实现了Map接口,这两个抽象实际上都可以看作是组合模式的OrganizationComponent(也就是我之前例子中的集团公司)。而有读过HashMap源码的朋友应该知道,在HashMap中有个及其关键的静态内部类Node。
相关源码片:

	/*** Basic hash bin node, used for most entries.  (See below for* TreeNode subclass, and in LinkedHashMap for its Entry subclass.)*/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;}}

它是组成链表/红黑树的原子结构(这里与本节无关暂不做展开,感兴趣的朋友可以再查阅下相关资料),hashMap的put方法、putAll方法都以Node为最小执行单位。这样来看,静态内部类Node更像是由HashMap泛化来的另一个实现形式(类似于我之前例子中的省公司、办事处)。这种叶子节点(Node)与子节点(HashMap)同根同源(Map),与组合模式理念“不谋而合”。
在这里插入图片描述

相关源码片:

	/*** Associates the specified value with the specified key in this map.* If the map previously contained a mapping for the key, the old* value is replaced.** @param key key with which the specified value is to be associated* @param value value to be associated with the specified key* @return the previous value associated with <tt>key</tt>, or*         <tt>null</tt> if there was no mapping for <tt>key</tt>.*         (A <tt>null</tt> return can also indicate that the map*         previously associated <tt>null</tt> with <tt>key</tt>.)*/public V put(K key, V value) {return putVal(hash(key), key, value, false, true);}/*** Implements Map.put and related methods.** @param hash hash for key* @param key the key* @param value the value to put* @param onlyIfAbsent if true, don't change existing value* @param evict if false, the table is in creation mode.* @return previous value, or null if none*/final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {Node<K,V>[] tab; Node<K,V> p; int n, i;if ((tab = table) == null || (n = tab.length) == 0)n = (tab = resize()).length;if ((p = tab[i = (n - 1) & hash]) == null)tab[i] = newNode(hash, key, value, null);else {Node<K,V> e; K k;if (p.hash == hash &&((k = p.key) == key || (key != null && key.equals(k))))e = p;else if (p instanceof TreeNode)e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);else {for (int binCount = 0; ; ++binCount) {if ((e = p.next) == null) {p.next = newNode(hash, key, value, null);if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1sttreeifyBin(tab, hash);break;}if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k))))break;p = e;}}if (e != null) { // existing mapping for keyV oldValue = e.value;if (!onlyIfAbsent || oldValue == null)e.value = value;afterNodeAccess(e);return oldValue;}}++modCount;if (++size > threshold)resize();afterNodeInsertion(evict);return null;}/*** Copies all of the mappings from the specified map to this map.* These mappings will replace any mappings that this map had for* any of the keys currently in the specified map.** @param m mappings to be stored in this map* @throws NullPointerException if the specified map is null*/public void putAll(Map<? extends K, ? extends V> m) {putMapEntries(m, true);}/*** Implements Map.putAll and Map constructor.** @param m the map* @param evict false when initially constructing this map, else* true (relayed to method afterNodeInsertion).*/final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {int s = m.size();if (s > 0) {if (table == null) { // pre-sizefloat ft = ((float)s / loadFactor) + 1.0F;int t = ((ft < (float)MAXIMUM_CAPACITY) ?(int)ft : MAXIMUM_CAPACITY);if (t > threshold)threshold = tableSizeFor(t);}else if (s > threshold)resize();for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {K key = e.getKey();V value = e.getValue();putVal(hash(key), key, value, false, evict);}}}

组合模式依据树形结构来组合对象,从部分到整体层次分明,是典型的结构型模式。组合模式对用户使用的单个对象表现出极强的一致性,对客户而言无需特别关注个别对象和组合对象。
希望此文能够让大家对组合模式有更进一步的理解。


关注我,共同进步,每周至少一更。——Wayne

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

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

相关文章

mysql在服务器中的主从复制Linux下

mysql在服务器中的主从复制Linux下 为什么要进行主从复制主从复制的原理主从复制执行流程操作步骤主库创建从库创建 测试 为什么要进行主从复制 在业务中通常会有情况&#xff0c;在sql执行时&#xff0c;将表锁住&#xff0c;导致不能进行查询&#xff0c;这样就会影响业务的…

游戏平台如何定制开发?

随着科技的飞速发展和互联网的普及&#xff0c;游戏平台已成为人们休闲娱乐的重要选择。为了满足用户多样化的需求&#xff0c;游戏平台的定制开发显得尤为重要。本文将探讨游戏平台定制开发的过程、关键要素以及注意事项&#xff0c;为有志于涉足此领域的开发者提供参考。 一、…

商品评论接口的应用

一、应用场景 商家调研自家产品的满意度及改进建议&#xff0c;B端商户想要铺货挑选商品&#xff0c;独立站运营商 二、公共参数 请求地址: https://api/item_review 三、请求参数 请求参数&#xff1a;num_iid600530677643&data&page1 参数说明&#xff1a;参数…

OpenAI文生视频大模型Sora概述

Sora&#xff0c;美国人工智能研究公司OpenAI发布的人工智能文生视频大模型&#xff08;但OpenAI并未单纯将其视为视频模型&#xff0c;而是作为“世界模拟器” &#xff09;&#xff0c;于2024年2月15日&#xff08;美国当地时间&#xff09;正式对外发布。 Sora可以根据用户…

golang入门介绍-1

今天开始发布关于go语言入门到实战内容&#xff0c;各位小伙伴准备好。 go介绍 Go语言&#xff08;或 Golang&#xff09;起源于 2007 年&#xff0c;并在 2009 年正式对外发布。是由 Google 公司开发的一种静态强类型、编译型、并发型、并具有垃圾回收功能的编程语言。 Go 是…

Maven depoly:Skipping artifact deployment

问题描述&#xff1a; 使用IDEA执行mvn depoly将本地开发的模块发布到Maven私服时&#xff0c;一直提示&#xff1a;Skipping artifact deployment&#xff0c;自动跳过了depoly部署阶段。 问题分析 Maven构建生命周期中的每一个阶段都是由对应的maven插件执行具体工作的。既然…

【无标题】旋转链表与力扣报错:member access within null pointer of type ‘struct ListNode‘

项目场景&#xff1a; 做单链表反转题目&#xff0c;报错&#xff1a;member access within null pointer of type ‘struct ListNode’ 题目链接:LINK 问题描述 我明明在初始化指针时候&#xff0c;已经处理了n2->next情况却依然报错 这个报错提示含义是&#xff1a;大概就…

C++日志库plog使用指南

前言 之前介绍过一个C语言日志库 轻量级c语言开源日志库log.c介绍&#xff0c;源代码只有不到200行&#xff0c;使用非常方便。但是也存在很多缺点&#xff0c;比如日志时间只支持打印到秒&#xff0c;没有作多线程处理&#xff0c;不支持日志回滚。在小型项目或者测试demo中使…

【Effective Objective - C】—— block 块

【Effective Objective - C】—— block 块 前言37.理解块的概念块的基础知识块可以捕获变量内联块的用法块的内部结构栈块堆块全局块要点 38.为常用的块类型创建typedef要点 39.用handler块降低代码分散程度协议传值实现异步块实现异步回调操作里的块要点 40.用块引用其所属对…

SpringBoot项目快速创建

SpringBoot项目快速创建 方法一&#xff1a;通过IDEA的Spring Initializr 点击文件&#xff0c;新建项目&#xff0c;选择Spring Initializr 名称&#xff1a;项目名称存放位置&#xff1a; 语言&#xff1a;Java类型&#xff1a;Maven组&#xff1a;<groupId>com.exam…

MyBatisPlus条件构造器和常用接口

前置配置文章 一、wapper介绍 wrapper的继承体系&#xff1a; Wrapper &#xff1a; 条件构造抽象类&#xff0c;最顶端父类 AbstractWrapper &#xff1a; 用于查询条件封装&#xff0c;生成 sql 的 where 条件 QueryWrapper &#xff1a; 查询条件封装UpdateWrapper &#x…

【MATLAB GUI】 4. 坐标区和表

看B站up主freexyn的freexyn编程实例视频教程系列36Matlab GUI的学习笔记 文章目录 坐标区表 坐标区 任务要求设计一个图像显示界面&#xff0c;根据选定的周期做出相应的sin函数图像 使用坐标区、弹出式菜单、普通按钮设计页面&#xff0c;弹出式菜单string设置为1、2、3、4代…

C#,动态规划(DP)丢鸡蛋问题(Egg Dropping Puzzle)的三种算法与源代码

1 扔鸡蛋问题 动态规划&#xff08;Dynamic Programming&#xff0c;DP&#xff09;是运筹学的一个分支&#xff0c;是求解决策过程最优化的过程。20世纪50年代初&#xff0c;美国数学家贝尔曼&#xff08;R.Bellman&#xff09;等人在研究多阶段决策过程的优化问题时&#xf…

【MySQL】数据库概述

目录 一、为什么使用数据库&#xff1f; 二、数据库与数据库管理系统 2.1 相关概念 2.2 两者关系 三、 MySQL介绍 四、 RDBMS和非RDBMS 4.1 关系型数据库&#xff08;RDBMS&#xff09; 4.2 非关系型数据库&#xff08;非RDBMS&#xff09; 五、关系型数据库设计规则 …

WebService学习,wsdl文件详解

目录 第一章、起因1.1&#xff09;学习原因1.2&#xff09;提问的过程&#xff08;逐步提出问题&#xff09;1、&#xff1f;wsdl链接的含义&#xff0c;有什么作用&#xff1f;2、什么是wsdl文档&#xff1f;3、如何阅读wsdl文件&#xff1f;4、wsdl文件有什么作用&#xff1f…

百面嵌入式专栏(经验篇)如何在面试中介绍自己的项目经验

文章目录 1. 在面试前准备项目描述,别害怕,因为面试官什么都不知道2. 准备项目的各种细节,一旦被问倒了,就说明你没做过3.不露痕迹地说出面试官爱听的话4.一定要主动,面试官没有义务挖掘你的亮点5.一旦有低级错误,可能会直接出局6.引导篇:准备些加分点,在介绍时有意提到…

图文说明Linux云服务器如何更改实例镜像

一、应用场景举例 在学习Linux的vim时&#xff0c;我们难免要对vim进行一些配置&#xff0c;这里我们提供一个vim插件的安装包&#xff1a; curl -sLf https://gitee.com/HGtz2222/VimForCpp/raw/master/install.sh -o./install.sh && bash ./install.sh 但是此安装包…

关于el-select值的回显问题 : 框内显示label值还是value值

<el-form-item label"状态" prop""><el-selectv-model"roleForm.state"class"m-2"size"large"style"width: 240px"placeholder"请选择状态"value-key"value"//value-key 与下面的ke…

区块链笔记(五)---德勤相关分析报告

web3.0 定义&#xff1a; 在《Insights into a Modern World》提出&#xff0c;“信息将由用户自己发布、保管、不可追溯且永远不会泄露&#xff0c;用户的任何行为将不需要任何中间机构来帮助传递”&#xff1b;用来指代一种区块链技术&#xff0c;可以基于“无须信任的交互…

探索D咖智能饮品机器人的工作原理:科技、材料与设计的相互融合

智能饮品机器人是近年来随着人工智能和自动化技术的发展而崭露头角的一种创新产品。它将科技、材料和设计相互融合&#xff0c;为消费者带来了全新的饮品体验。下面D咖来探索智能饮品机器人的工作原理&#xff0c;以及科技、材料和设计在其中的作用。 首先&#xff0c;智能饮品…