设计模式 ——— 中介者模式

意图

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

中介者模式的本质:封装交互。

适用性

  • 如果一组对象之间的通信方式比较复杂,导致相互依赖、结构混乱,可以采用中介者模式,把这些对象相互的交互管理起来,各个对象都只需要和中介者交互,从而使得各个对象松散耦合,结构也更清晰易懂。
  • 如果一个对象引用很多的对象,并直接跟这些对象交互,导致难以复用该对象。可以采用中介者模式,把这个对象跟其它对象的交互封装到中介者对象里面,这个对象就只需要和中介者对象交互就可以了。

结构

中介者模式结构图

  • Mediator(中介者接口) 在里面定义各个同事之间交互需要的方法,可以是公共的通讯方法,比如changed方法,大家都用,也可以是小范围的交互方法。
  • ConcreteMediator(具体中介者类) 它需要了解并维护各个同事对象,并负责具体的协调各同事对象的交互关系。
  • Colleague(同事类) 通常实现成为抽象类,主要负责约束同事对象的类型,并实现一些具体同事类之间的公共功能,比如:每个具体同事类都应该知道中介者对象,也就是具体同事类都会持有中介者对象,就可以定义到这个类里面。
  • ConcreteColleague(具体同事类) 具体的同事类,实现自己的业务。每一个同事类都知道它的中介者对象。每一个同事对象在需与其他的同事通信的时候,与它的中介者通信。

场景解说

下面我们通过一个场景来让大家更容易对中介者模式有一个直观的认识。 场景: 感谢未来屋公司的这群好家伙,Bob拥有一个Java版本的自动屋,这可以让他的生活变得更便利。当Bob点击了打盹按钮,他的闹钟就会告诉咖啡壶开始煮咖啡。尽管生活对他来说是如此惬意,但他(以及其他的客户)总是不断地提出许多新的要求:周末不要供应咖啡......在洗澡前将喷头关闭15分钟......在丢垃圾的日子里将闹钟时刻提前......

问题:想要持续地追踪每个对象的每个规则,以及众多对象之间彼此错综复杂的关系,实在不容易。 解决方案:使用中介者模式 仔细分析上面的问题,根本原因就在于多个对象需要相互交互,从而导致对象之间紧密耦合,这就不利于对象的修改和维护。 在这个系统中加入一个中介者,一切都变得简单了。

  • 每个对象都会在自己的状态改变时,告诉中介者
  • 每个对象都会对中介者所发出的请求作出回应。

在没有中介者的情况下,所有的对象都需要认识其他对象......也就是说,对象之间是紧耦合的。有了中介者之后,对象之间彻底解耦。 中介者内包含了整个系统的控制逻辑。当某装置需要一个新的规则时,或者是一个新的装置被加入系统内,其所有需要用到的逻辑也都被加进了中介者内。

对中介者模式实现的深入探讨

  • 忽略抽象的Mediator类 有没有使用Mediator接口的必要,那就取决于是否会提供多个不同的中介者实现。如果中介者实现只有一个的话,而且预计中也没有需要扩展的要求,那么就可以不定义Mediator接口,让各个同事对象直接使用中介者实现对象;如果中介者实现不只一个,或者预计中有扩展的要求,那么就需要定义Mediator接口,让各个同事对象来面向中介者接口编程,而无需关心具体的中介者实现。
  • Colleague ———— Mediator 通信 Colleague 与 Mediator 之间通信有两种实现方式: ① 第一种实现方式是:使用观察者模式,将Mediator实现为一个Observer,各Colleague作为Subject,一旦其状态改变就发送通知给Mediator。Mediator作出的响应是将状态改变的结果传播给其他的Colleague。 ② 第二种实现方式是:在Mediator中定义一个特殊的通知接口,各Colleague在通信时直接调用该接口。

优缺点

优点

① 松散耦合 中介者模式通过把多个同事对象之间的交互封装到中介者对象里面,从而使得同事对象之间松散耦合,基本上可以做到互不依赖。这样一来,同事对象就可以独立的变化和复用,而不再像以前那样“牵一发而动全身”了。

② 集中控制交互 多个同事对象的交互,被封装在中介者对象里面集中管理,使得这些交互行为发生变化的时候,只需要修改中介者对象就可以了,而各个同事类不需要做修改。这使得你将注意力从对象各自本身的行为转移到它们之间的交互上来,这有助于弄清楚一个系统中的对象是如何交互的。

③ 多对多变成一对多 没有使用中介者模式的时候,同事对象之间的关系通常是多对多的,引入中介者对象过后,中介者对象和同事对象的关系通常变成了双向的一对多,这会让对象的关系更容易理解和实现。

缺点

① 过度集中化 中介者模式的一个潜在缺点是,如果同事对象的交互非常多,而且比较复杂,当这些复杂性全部集中到中介者的时候,会导致中介者对象变得十分的复杂,而且难于管理和维护。

广义中介者

仔细查看中介者的结构、定义和示例,会发现几个问题,使得中介者模式在实际使用的时候,变得繁琐或困难。 Q:是否有必要为同事对象定义一个公共的父类? 大家都知道,Java是单继承的,为了使用中介者模式,就让这些同事对象继承一个父类,这是很不好的;再说了,这个父类目前也没有什么特别的公共功能,也就是说继承它也得不到多少好处。 A:在实际开发中,很多相互交互的对象本身是没有公共父类的,强行加上一个父类,会让这些对象实现起来特别别扭。

Q:同事类有必要持有中介者对象吗? 同事类需要知道中介者对象,以便当它们发生改变的时候,能够通知中介者对象,但是,是否需要作为属性,并通过构造方法传入,这么强的依赖关系呢? A:也可以有简单的方式去通知中介对象,比如把中介对象做成单例,直接在同事类的方法里面去调用中介者对象。

Q:是否需要中介者接口? A:在实际开发中,很常见的情况是不需要中介者接口的,而且中介者对象也不需要创建很多个实例,因为中介者是用来封装和处理同事对象的关系的,它一般是没有状态需要维护的,因此中介者通常可以实现成单例。

Q:其四:中介者对象是否需要持有所有的同事? A:虽说中介者对象需要知道所有的同事类,这样中介者才能与它们交互。但是是否需要做为属性这么强烈的依赖关系,而且中介者对象在不同的关系维护上,可能会需要不同的同事对象的实例,因此可以在中介者处理的方法里面去创建、或者获取、或者从参数传入需要的同事对象。

Q:中介者对象只是提供一个公共的方法,来接受同事对象的通知吗? A:在实际开发中,通常会提供具体的业务通知方法,这样就不用再去判断到底是什么对象,具体是什么业务了。

实际开发中我们使用更多的是中介者模式的变种: 只要是实现封装对象之间的交互功能,就可以应用上中介者模式,而不必过于拘泥于中介者模式本身的结构。标准的中介者模式限制很多,导致能完全按照标准使用中介者模式的地方并不是很多,而且多集中在界面实现上。只要本质不变,稍稍变形一下,简化一下,或许能更好的使用中介者模式。

相关模式

中介者模式和外观模式

这两个模式有相似的地方,也存在很大的不同。

外观模式多用来封装一个子系统内部的多个模块,目的是向子系统外部提供简单易用的接口,也就是说外观模式封装的是子系统外部和子系统内部模块间的交互;而中介者模式是提供多个平等的同事对象之间交互关系的封装,一般是用在内部实现上。

另外,外观模式是实现单向的交互,是从子系统外部来调用子系统内部,不会反着来,而中介者模式实现的是内部多个模块间多向的交互。

中介者模式和观察者模式

这两个模式可以组合使用。 中介者模式可以组合使用观察者模式,来实现当同事对象发生改变的时候,通知中介对象,让中介对象去进行与其它相关对象的交互。

参考

《Head First 设计模式》 《设计模式:可复用面向对象软件的基础》 《研磨设计模式》

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏一枝花算不算浪漫

[Redis]Redis 概述及基本使用规范.

52980
来自专栏程序员互动联盟

【专业技术】Linux设备驱动第六篇:高级字符驱动操作之iotcl

在之前我们介绍了如何实现一个简单的字符设备驱动,并介绍了简单的open,close,read,write等驱动提供的基本功能。但是一个真正的设备驱动往往提供了比...

36680
来自专栏magicsoar

C++编译与链接(2)-浅谈内部链接与外部链接

发现每次写技术博客时,都会在文章开头处花费一番功夫 ...从前,有一个程序员....他的名字叫magicsoar 为什么有时会出现aaa已在bbb中重定义的错误...

310100
来自专栏海天一树

小朋友学Python(1):Python简介与编程环境搭建

一、Python简介 不死Java,不朽C/C++,新贵Python。 Python(英国发音:/ˈpaɪθən/ 美国发音:/ˈpaɪθɑːn/), 是一种面...

348110
来自专栏编程

PHP7 下的协程实现

前言 相信大家都听说过『协程』这个概念吧。 但是有些同学对这个概念似懂非懂,不知道怎么实现,怎么用,用在哪,甚至有些人认为yield就是协程! 我始终相信,如果...

38680
来自专栏coding for love

在线商城项目08-数据库创建和商品集合的创建

因为six_tao中没有任何内容。我们需要为其创建一个集合或者插入文档,数据库才会显示。例如:

8440
来自专栏码农二狗

防止因事务未提交导致的死锁

11140
来自专栏FreeBuf

Java反序列化漏洞从理解到实践

一、前言 在学习新事物时,我们需要不断提醒自己一点:纸上得来终觉浅,绝知此事要躬行。这也是为什么我们在学到知识后要付诸实践的原因所在。在本文中,我们会深入分析大...

337100
来自专栏于晓飞的专栏

理解 CRLF,LF

CRLF, LF 是用来表示文本换行的方式。CR(Carriage Return) 代表回车,对应字符 '\r';LF(Line Feed) 代表换行,对应字符...

29130
来自专栏阿杜的世界

Spring Boot:定制URL匹配规则

构建web应用程序时,并不是所有的URL请求都遵循默认的规则。有时,我们希望RESTful URL匹配的时候包含定界符“.”,这种情况在Spring中可以称之为...

15230

扫码关注云+社区

领取腾讯云代金券