设计模式——2_4 中介者(Mediator)

我寄愁心与明月,随风直到夜郎西

——李白《闻王昌龄左迁龙标遥有此寄》

文章目录

  • 定义
  • 图纸
  • 一个例子:怎么调度一组地铁
    • 站台和地铁
    • 开车
    • 指挥中心
  • 碎碎念
    • 中介者和表单
    • 平台思想
      • 但是这种平台便利性是要付出代价的
        • 变化隔离原则
    • 姑妄言之

定义

用一个中介者对象来封装一系列的对象交互。中介者使各个对象之间不需要显式的相互引用,从而使其耦合松散,而且可以独立地改变他们之间的交互




图纸

在这里插入图片描述




一个例子:怎么调度一组地铁

地铁,就是那种在地底下(也未必,深圳的11号线就能看海,听说重庆的地铁还能过楼?)沿着轨道跑的列车

一般来说一个车站会有两个不同方向的站台,而同一个方向的站台在同一时间显然只能有一部列车可以同时出现

那么当一列地铁即将到达某一个站台的时候,是需要确认目标站台上有没有地铁正在停靠的

当你把上述行为抽象成代码的时候,中介者可以帮助你优雅的实现对地铁进行调度的过程,而这正是我们这次的例子:



站台和地铁

无论如何,站台和地铁都一定有对应自己的类,就像这样:

在这里插入图片描述

/*** 站台*/
public class Platform {/*** 站台名称*/private String name;/*** 当前停靠的地铁*/private Subway subway;public Platform(String name) {this.name = name;}/*** 进站*/public synchronized void in(Subway subway) {System.out.printf("%s 即将进入 %s%n", subway.getCode(), name);this.subway = subway;System.out.printf("%s 进入了 %s%n", subway.getCode(), name);}/*** 进站*/public synchronized void out() {System.out.printf("%s 离开 %s%n", subway.getCode(), name);this.subway = null;}/*** 是否是空站*/public synchronized boolean isEmpty() {return subway == null;}
}/*** 地铁*/
public class Subway {private String code;public Subway(String code) {this.code = code;}public String getCode() {return code;}
}

我们新建了 Platform(站台)Subway(地铁) 两个类分别用于表示站台和地铁,站台上可以停靠地铁,而且通过 isEmpty 方法可以告诉 client 当前这个站台是否停靠了地铁


接着问题来了,client代码 要怎么指挥 Subway(地铁) 往前走呢?



开车

打个比方,现在我们有 站台A/B/C,有电灯号和灯笼号两部地铁,同时规定:

  1. 两部地铁都是沿着A->B->C这个方向往前移动

  2. 电灯号从站台A出发,灯笼号从站台B出发

首先我们要初始化他,就像这样:

Platform a = new Platform("A");
Platform b = new Platform("B");
Platform c = new Platform("C");Subway subway_1 = new Subway("电灯号");
Subway subway_2 = new Subway("灯笼号");

接着我们让电灯号进入A站台,再让灯笼号进入A站台;这时候因为电灯号还在站台里,所以程序应该提示我 不能进入。接着让灯笼号离开A站台,再让电灯号进入,就像这样:

private static Map<Subway, Integer> subwayMap = new HashMap<>();//用于记录地铁的位置
private static Platform[] ps;public static void main(String[] args) {//初始化ps = new Platform[]{new Platform("A"), new Platform("B"), new Platform("C")};Subway subway_1 = new Subway("电灯号");Subway subway_2 = new Subway("灯笼号");subwayMap.put(subway_1, 0);ps[0].in(subway_1);subwayMap.put(subway_2, 1);ps[1].in(subway_2);move(subway_1);//电灯号往前走,被挡住move(subway_2);//灯笼号往前走move(subway_1);//电灯号往前走
}public static void move(Subway subway) {Integer position = subwayMap.get(subway);int nextPosition = position + 1 < ps.length ? position + 1 : 0;if (ps[nextPosition].isEmpty()) {//空站台可以驶入ps[position].out();//驶出ps[nextPosition].in(subway);//驶入subwayMap.put(subway, nextPosition);} else {System.out.println("还有车,无法驶入");}
}

在这里插入图片描述

这段代码有两个问题:

  1. 里面出现了可以抽离出来的部分,也就是move方法

  2. 我们向 client 暴露了站台的内部结构,在实战中,你一定不希望这种事的发生


事实上这两个问题都可以通过创建一个平台来解决。于是乎,为了解决这样的问题,我们引入了 指挥中心 的概念


指挥中心

就像这样:

/*** 指挥中心*/
public class ControlCenter {private Map<Subway, Integer> subwayMap = new HashMap<>();//用于记录地铁的位置private Platform[] ps = new Platform[]{new Platform("A"), new Platform("B"), new Platform("C")};/*** 推动某部地铁往前走*/public void move(Subway subway) {Integer position = subwayMap.get(subway);int nextPosition = position + 1 < ps.length ? position + 1 : 0;if (ps[nextPosition].isEmpty()) {//空站台可以驶入ps[position].out();//驶出ps[nextPosition].in(subway);//驶入subwayMap.put(subway, nextPosition);} else {System.out.println("还有车,无法驶入");}}public void addSubway(Subway subway, int position) {subwayMap.put(subway, position);ps[position].in(subway);}
}
 public static void main(String[] args) {ControlCenter controlCenter = new ControlCenter();Subway s1 = new Subway("电灯号");Subway s2 = new Subway("灯笼号");controlCenter.addSubway(s1,0);controlCenter.addSubway(s2,1);controlCenter.move(s1);//电灯号往前走,被挡住controlCenter.move(s2);//灯笼号往前走controlCenter.move(s1);//电灯号往前走}

我们把上面所说的内容抽象到了 ControlCenter(控制中心) 中,让 Subway 对于 Platform 有关的变动不要自己去操作,而是让 ControlCenter 代劳,从而实现对内容和关系的隐藏,以及集中化管理

而这正是一个标准的中介者实现


可能这个例子过于简单,没能把中介者的威力完全体现。事实上在实际开发中,当你的某个局部内的各个组件之间关联非常密切的时候,中介者的存在是不可或缺的。他让你可以从上层俯瞰所有组件之间的结构,而不是在各个组件中去找某个动作实现后会对谁造成影响




碎碎念

中介者和表单

表单,应该是程序设计历史上第一种人机交互方式,也是最常用的交互形式

而表单中的内容通常会有很多级联操作,比如说:

  • 密码框和重复密码框,如果两者输入不一致,我应该提示用户吧
  • 级联下拉框,选择第一级后,后面的下拉框里的内容需要被修改吧
  • 点击重置按钮,已经填的所有信息都应该被清空吧

问题在于,类似这些 在一个控件中,对另一个或几个控件进行操作的业务代码,应该写到哪里去呢?

第一个思路 就是让对象间自己进行交互,那显然不现实。这就意味着一个 重置 按钮对象 必须要获得当前表单内所有控件的引用,那我还怎么复用他?他的逻辑会变得很复杂,因为不同的控件会有不同的重置方式,甚至相同控件在不同的状态下也有不同的重置方式

更优解 其实就是中介者,而且这个中介者很好找,表单自身对象就可以来做这个中介者。可以让表单内的所有对象都来和这个表单对象进行交互,比如说:

  • 密码框和重复密码框输入完后发送信息给表单对象通知他验证
  • 选择第一级级联下拉框后通知表单对象变化下一级级联下拉框
  • 点击重置按钮后,通知表单对象重置表单数据

至此,表单内主体变化对象和被驱动变化的表单对象之间的耦合被解除了,因为只有表单对象需要知道每个操作到底涉及到了多少控件



平台思想

几乎所有的设计模式出现的初衷都是为了降低对象之间的耦合。我们一直讲代码要高内聚、低耦合,高耦合就意味着难以维护,好像一切都是耦合的罪过。既然如此,那我们不禁要问:

耦合可以被消灭吗?


答案是否定的,因为一定程度的耦合是必须的。对象是不可能完全独立、不依赖任何其他对象的。一点耦合都没有的代码,什么事情都完成不了


可是在实践中我们发现,具体对象之间的关联会让我们的系统结构变得复杂(如果画图的话,画出来的效果就像是一个纵横交错的网)

在我们维护系统的时候,尝试勾连出这样子的网的时候,这会让我们死很多脑细胞

所以作为一个热爱生命的人,我们引入了平台思想,让N个相互之间存在关联的对象,尽可能都和同一个对象打交道,然后在这个平台里集中处理一些关联信息,亦或是分发信息


这种设计思路非常非常非常的常见,无论是之前文章里出现过的 工厂方法(Factory Method)、抑或是外观(Facade),又或者是之后会出现的访问者(Visitor) 都涉及到了这种思想,同时这种思想还是IOC框架实现的基础


但是这种平台便利性是要付出代价的

随着系统的扩大,这个负责对象交互的平台一定会愈发复杂,而这部分 复杂 其实就是原本各自对象之间要进行的交互。也就是说,使用平台并不是彻底消灭了 复杂,而是把他们集中起来处理

这是符合设计原则的,因为其中有一条是这样写的:

变化隔离原则

找出应用中可能需要变化的地方,把他们独立出来,不要和那些不需要变化的代码混合在一起

依据这个原则,所以我们把对象之间的交互和对象自身要处理的业务进行隔离。因为对象之间的交互总是充满不确定性的,而对象自身的业务通常是在编码时就已经确定的



姑妄言之

说白了,中介者其实就是一个跟所有人都有关联的对象。那其实我们的古人早就提到过中介者这样的概念,那时的中介者,通常是我们头顶的月亮。李白就写过:我寄愁心与明月,随风直到夜郎西 这样的诗句,其实就是让大家都共享的月亮帮他传递信息嘛。

所以哪怕人真的是孤岛,又怎么可能真的那么孤单。只要仰望夜空,就一定有人此时此刻和你一起在同一片星空下仰望同一个月亮




万分感谢您看完这篇文章,如果您喜欢这篇文章,欢迎点赞、收藏。还可以通过专栏,查看更多与【设计模式】有关的内容

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

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

相关文章

抖店月销过万的爆单技巧,新手轻松月入1w+,附抖店学习资料!

我是电商珠珠 抖店开通之后&#xff0c;怎么才能快速出单是很多新手小伙伴困扰的问题。其实想要运营好抖店一点都不难&#xff0c;我做抖店也有三年多时间了&#xff0c;接下来我说的每一步&#xff0c;不管是有货源还是无货源的都适用。 1、铺货低价福利款 店铺开好之后&am…

ABAP 因去重和汇总导致金额数值错误

在去重之前&#xff0c;就有两条重复的&#xff0c;一旦进行分组汇总&#xff0c;金额就会翻倍&#xff0c;之后又进行去重&#xff0c;也是没有用的&#xff0c;错误数据是2588.6&#xff0c;是1294.3的两倍&#xff0c;现在试试先去重&#xff0c;再去计算数据 就是因为去重…

JAVA虚拟机实战篇之内存调优[4](内存溢出问题案例)

文章目录 版权声明修复问题内存溢出问题分类 分页查询文章接口的内存溢出问题背景解决思路问题根源解决思路 Mybatis导致的内存溢出问题背景问题根源解决思路 导出大文件内存溢出问题背景问题根源解决思路 ThreadLocal占用大量内存问题背景问题根源解决思路 文章内容审核接口的…

IP定位技术在金融风控中的应用研究

随着金融科技的快速发展&#xff0c;金融行业的风险也呈现出多样化、复杂化的特点。金融风控作为保障金融安全的重要手段&#xff0c;其面临的挑战也日益加剧。在这样的背景下&#xff0c;IP定位技术作为一种先进的信息技术手段&#xff0c;正逐渐成为金融风控领域的重要工具。…

会声会影软件界面字体太小 会声会影字体放大教程

会声会影做为一款经典且流行的剪辑软件&#xff0c;凭借其较低的入门门槛&#xff0c;直观的操作和丰富的功能&#xff0c;一直以来被很多用户所喜爱&#xff0c;这其中有部分是老年用户&#xff0c;他们可能因为视力较差&#xff0c;无法看清会声会影软件界面的文字的难题。今…

Dockerfile的使用,怎样制作镜像

Docker 提供了一种更便捷的方式&#xff0c;叫作 Dockerfile docker build命令用于根据给定的Dockerfile构建Docker镜像。 docker build命令参数&#xff1a; --build-arg&#xff0c;设置构建时的变量 --no-cache&#xff0c;默认false。设置该选项&#xff0c;将不使用Build …

C++指针(四)万字图文详解!

个人主页&#xff1a;PingdiGuo_guo 收录专栏&#xff1a;C干货专栏 前言 相关文章&#xff1a;C指针&#xff08;一&#xff09;、C指针&#xff08;二&#xff09;、C指针&#xff08;三&#xff09; 本篇博客是介绍函数指针、函数指针数组、回调函数、指针函数的。 点赞破六…

本鲸:打造科技招商新引擎、实现政企资源高效对接

在当今这个快速变化的时代&#xff0c;科技创新已成为推动社会进步和经济发展的核心动力。本鲸&#xff0c;作为科技创新创业服务的平台&#xff0c;正以其独特的视角和专业服务&#xff0c;为政府和企业提供一站式科技招商解决方案&#xff0c;助力构建创新驱动的经济发展新模…

鸿蒙Harmony应用开发—ArkTS声明式开发(基础手势:ImageAnimator)

提供帧动画组件来实现逐帧播放图片的能力&#xff0c;可以配置需要播放的图片列表&#xff0c;每张图片可以配置时长。 说明&#xff1a; 该组件从API Version 7开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 子组件 无 接口 ImageAni…

MySql 组合索引的使用

MySql 组合索引的使用 测试Mysql组合索引在不同的查询条件组合下的索引使用情况。当有abc 3个字的的组合索引时&#xff0c;按照MySql 的左匹配原则&#xff0c;abc&#xff0c;ab&#xff0c;a 是满足左匹配原则&#xff0c;肯定是会走索引的&#xff0c;但是其他的场景&…

ubuntu 命令行配置WiFi 密码

文章目录 一、命令行设置WiFi密码的意义二、Ubuntu的命令行设置WiFi密码步骤 一、命令行设置WiFi密码的意义 提供了一种不依赖图形界面的方式来配置WiFi连接。这对于那些没有图形界面或者需要远程配置WiFi的情况非常有用。通过命令行设置WiFi密码&#xff0c;可以方便地在终端中…

“一键批量自定义重命名:轻松驾驭文件海洋,让管理变得如此简单!“

在信息爆炸的时代&#xff0c;我们每天都会接触到大量的文件&#xff0c;从文档、图片到视频、音频等&#xff0c;各种类型、各种格式的文件充斥着我们的电脑和移动设备。如何有效地管理和组织这些文件&#xff0c;成为了我们不得不面对的问题。今天&#xff0c;我们为您带来了…

在vue2中使用tailwindcss(完整教程)

如果你看过好多教程之后&#xff0c;还是报错&#xff0c;无法使用tailwindcss&#xff0c;我希望本教程可以让你成功上岸。 环境要求 node&#xff1a;>v14.17.0 安装tailwindcss 由于最新的tailwind css使用post css 8版本&#xff0c;vue2框架暂时还不支持&#xff0…

使用docker安装运行rabbitmq---阿里云服务器

目录 0、阿里云没开端口的得要去安全组规则去添加&#xff1a; 1、下载RabbitMQ镜像&#xff1a; 2、查看镜像是否下载成功&#xff0c;得到docker镜像id&#xff1a; 3、运行RabbitMQ: 4、查看RabbbitMQ容器是否启动成功&#xff1a; 5、启动RabbitMQ中的插件管理 6、访…

混合输入矩阵乘法的性能优化

作者 | Manish Gupta OneFlow编译 翻译&#xff5c;宛子琳、杨婷 AI驱动的技术正逐渐融入人们日常生活的各个角落&#xff0c;有望提高人们获取知识的能力&#xff0c;并提升整体生产效率。语言大模型&#xff08;LLM&#xff09;正是这些应用的核心。LLM对内存的需求很高&…

LeetCode148题:排序链表(python3)

在数组排序中&#xff0c;常见的排序算法有&#xff1a;冒泡排序、选择排序、插入排序、希尔排序、归并排序、快速排序、堆排序、计数排序、桶排序、基数排序等。 而对于链表排序而言&#xff0c;因为链表不支持随机访问&#xff0c;访问链表后面的节点只能依靠 next 指针从头…

Flip Clock(not good)

最近体验了一下iOS的翻页时钟app&#xff0c;很想自己做一个&#xff0c;但是效果不好 public class main {public static void main(String[] args) {//psvmnew MyFrame();} }import javax.swing.*; import java.awt.*; import java.io.File; import java.io.IOException; im…

Vue的HTML插入——v-html指令

有时我们希望将数据作为HTML代码插入到HTML模板中&#xff0c;而不是以纯文本的形式显示。在这种情况下&#xff0c;我们需要使用Vue.js的v-html指令&#xff1a; <template><div><p>纯文本: {{ rawText }}</p><p>属性: <span v-html"r…

influxdb2.0插入数据字段类型出现冲突问题解决

一、问题出现 一个学校换热站自控系统&#xff0c;会定时从换热站获取测点数据&#xff0c;并插入到influxdb数据库中。influxdb插入数据时&#xff0c;报错提示&#xff1a; com.influxdb.exceptions.UnprocessableEntityException: failure writing points to database: par…

AlexNet 网络结构详解

一、基本了解 什么是过拟合&#xff1f; 解决方法 AlexNet网络结构通过使用dropout方法&#xff0c;使一些神经元失活&#xff0c;变相的减少了网络训练的参数化&#xff0c;从而实现减少过拟合。 二、AlexNet网络结构的详细解释 他是由上下两组GPU进行运算的&#xff0c;所以…