用一个中介对象(中介者)来封装一系列的对象交互,中介者使各对象不须要显式地相互引用,从而使其耦合松散
并且能够独立地改变它们之间的交互。
中介者模式又称为调停者模式。
面向对象的程序设计中,咱们一般将功能进行分解,按照职责以类为维度进行划分,也就是使用时功能最终将分布在多个对象中
而且咱们会尽量的保持对象功能的单一(单一职责原则)
相对于对象的单一职责来讲,任何的系统或者模块的功能却并不会单一,每每都是有多个对象交互协做来实现全部的功能
对象之间不可避免的须要创建链接
换句话说
系统(或者某模块)必然是复杂的(没有什么系统能够几个对象就轻松搞定,那样或许也不能称之为系统了吧)
功能必然会分布在多个对象中
多个对象协做必然须要联系,这必然致使耦合的产生
image_5c171b2a_724a
如上图所示,虽然系统对外呈现是一个统一的总体,可是,内部各个模块之间极可能是紧密的耦合
各个模块相互联系,可能互相持有引用,会出现网状结构,彻底不符合迪米特法则。
若是对系统进行改动,将会变得困难。
咱们以装修为例
通常装修公司都会给每个项目配备一个项目经理(这个项目也就是你家这个单子了,项目经理就是包工头)
装修的通常阶段分为:前期设计→拆改→水电→瓦工→木工→油漆→安装→保洁→软装
项目经理手上常常同时有几个工地在同步进行,只要错的开就行了
由于每一个阶段都是有前后顺序的,你不可能先木工,而后再去拆改;
由于每一个阶段也都须要必定时间,也意味着这一拨人不可能同时在你家工做
开工后项目经理会进行工做安排
水电工结束了A以后,项目经理会安排他到B,而后安排瓦工到A,而后........
全部的顺序都是由项目经理负责调度,水电工能够彻底不认识瓦工,他们也彻底不须要进行联系
有事儿找项目经理
若是没有项目经理,会是什么场景?
那就是人人都是项目经理,人人都须要管本身,还须要管别人
也就是每一个人安排分配本身的时间与任务
水电工结束后须要联系瓦工进场,若是瓦工发现有遗留问题,须要联系水电工进行沟通
木工须要联系瓦工确认进展状况,油漆工又须要确认木工情况...
你会发现他们必需要常常保持联系,以得到进展状况,进而安排本身的工做
一个包工队尚且如此,若是是一个大的装修公司,怎么办?
并且装修而言,阶段之间还会有顺序,油漆工用不到联系水电工
可是在系统中,对象岂会仅仅与一个对象联系?
那岂不是更复杂、乱套?
中介者模式就是为了解决系统内部的调度问题,下降系统内部各模块之间的耦合度。
装修公司的项目经理、小组组长、班长,团队leader等其实这都是中介者模式的体现。
有不少书中以“房屋中介”做为中介者模式的一种场景
我的认为对于某一个房东或者租客而言,“房屋中介”的含义是为你服务的中介人员,此时的含义更接近代理模式
而从广义上看,有不少租客、买家,也存在不少房东,“房屋中介”将他们联系在一块儿,此时的“房租中介”应该是中介公司,这时才更符合中介者模式的含义
中介者模式的重点在于“调度、协调”,含义更接近“指挥中心”,被指挥的是该系统内部的成员
若是在一个系统中对象之间存在多对多的相互关系
咱们能够将对象之间的一些交互行为从各个对象中分离出来,并集中封装在一个中介者对象中,并由该中介者进行统一协调
image_5c171b2a_66af
如上图所示,对象之间多对多的复杂关系就转化为相对简单的一对多关系
简化了对象之间的复杂交互
显然,中介者模式是迪米特法则(不要和陌生人说话)的典型。
结构
image_5c171b2a_260e
同事角色Colleague
系统中全部组成部件的抽象角色
具体的同事角色ConcreteColleague
系统的每个具体的组成部分,就像公司的每一个同事
提供自身职责功能的方法接口,供中介者调用
定义中介者到同事对象的接口,也就是提供接口给中介者调用
中介者(项目经理)根据你的技能分配任务,也就是调用你的方法
中介者角色Mediator
定义了同事Colleague对象到中介者的接口,也就是全部同事通讯的接口(同事间的通讯借助于中介者提供的这个方法)
也就是提供一个方法给同事们调用,用来请求其余同事协助协助,这个方法是中介者提供的
这个方法典型的示例就是事件处理方法
具体的中介者ConcreteMediator
具体的中介者,实现Mediator定义的接口,协调各同事进行协做
全部的成员之间,能够相互协调工做,可是却又不直接相互管理
这些对象都与项目经理“中介者”进行紧密联系
由项目经理进行工做协调,每一个组成部分就如同咱们项目组中的一个成员,也就是同事同样,这也是上文中Colleague 角色的由来
如何相互协调工做可是却又不直接相互管理?好比
复制代码
class A{
void f(){
//do sth
B b = new B();
b.g();
}
复制代码
上面伪代码中
类A有一个方法f ,作了一些事情以后,建立了一个B的对象b,而后调用b的方法g,作了一些处理
这就是A与B的协做,A也同时具备管理B的职责
若是转换为下面的形式,就是中介者模式
A和B的协做不在具备对象管理关系,而是项目经理Mediator统一进行管理
复制代码
class Mediator{
A a = new A();
B b = new B();
void cooperation(){
a.f();
b.g();
}
}
复制代码
代码示例
使用《设计模式 可复用面向对象软件的基础》中的例子为原型
考虑一个图形用户界面中对话框的实现。
对话框使用一个窗口来展示一系列的窗口组件,好比按钮菜单输入域等
好比下图,IDEA的字体设置窗口,当进行Font字体设置时
预览区域内的字体将会发生变化
右下角的Apply 应用按钮将成为可点击状态
image_5c171b2b_350a
一种可能的解决方法
复制代码
package mediator.simple;
/**
* 设置字体类,提供字体设置方法.
* 而且建立展现Display对象,调用reDisplay方法从新展现
* 而且建立按钮Button对象,调用applyButton方法使能应用按钮
*/
public class Font {
public void setFont() {
System.out.println("设置字体...");
Display display = new Display();
display.reDisplay();
Button button = new Button();
button.applyButton();
}
}
复制代码
复制代码
package mediator.simple;
public class Display {
public void reDisplay() {
System.out.println("字体从新展现...");
}
}
复制代码
复制代码
package mediator.simple;
public class Button {
public void applyButton() {
System.out.println("应用按钮可用...");
}
}
复制代码
复制代码
package mediator.simple;
public class Test {
public static void main(String[] args) {
Font font = new Font();
font.setFont();
}
}
复制代码
image_5c171b2b_398c
上面的示例很简单
为了实现“点击设置字体,选择字体后预览框字体的改变以及使能应用按钮的功能”
也就是联动的功能
设置字体后,分别建立展现和按钮对象,调用对象的方法
很显然,字体不只操心本身的事情,还管理着展现Display和按钮Button
并且,若是直接点击取消会发生什么?一切将会还原,又伴随着一系列的调用
难道仍旧须要:“不只操心本身的事情,还要负责管理别人么”?
就像没有项目经理的包工队同样了,既操心本身又要管理别人
成了咱们上面所说的网状结构,内部各个同事之间的耦合度极高
重构中介者模式
重构的业务逻辑:
经过引入mediator中介者,做为同事之间协做的中间人,提供operation()方法,用于同事间请求协助、事件处理
每一个同事类都知道这个中介,因此在抽象角色Colleague中设置了Mediator属性,构造方法注入,而且提供notifyEvent方法,封装了mediator的operation()方法
当具体的同事ConcreteColleague,执行操做后,须要其余同事协做时,直接调用notifyEvent()方法
每一个具体的同事提供自身的职责接口
简单地说就是,提供中介者“项目经理”mediator,提供事件处理方法
全部的同事都只知道“项目经理”,若是有事须要其余同事办,就叫“项目经理”。
Mediator抽象中介者角色定义了operation方法
各个Colleague对象经过此方法进行通讯,接受参数类型为Colleague的对象
package mediator;
public abstract class Mediator {
abstract void operation(Colleague event);
}
Colleague抽象同事角色拥有Mediator,经过构造方法注入
提供了notifyEvent方法,调用中介者的operation方法,而且将自身做为参数
复制代码
package mediator;
public abstract class Colleague {
private Mediator mediator;
Colleague(Mediator mediator) {
this.mediator = mediator;
}
public void notifyEvent() {
mediator.operation(this);
}
}
复制代码
复制代码
package mediator;
public class Button extends Colleague {
Button(Mediator mediator){
super(mediator);
}
public void applyButton(www.dasheng178.com/ ) {
System.out.println("应用按钮可用...");
}
}
复制代码
复制代码
package mediator;
public class Display extends Colleague {
Display(Mediator www.michenggw.com/ mediator) {
super(mediator);
}
public void reDisplay(www.mcyllpt.com/ ) {
System.out.println("字体从新展现...");
}
}
复制代码
复制代码
package mediator;
public class Font extends Colleague {
private String fontName;
public String getFontName(www.gcyl152.com) {
return fontName;
}
Font(Mediator mediator) {
super(mediator);
}
public void changeFont() {
System.out.yongshiyule178.com/ println("设置字体......");
fontName = "微软雅黑";
notifyEvent();
}
}
复制代码
ConcreteMediator实现了Mediator定义的接口
而且内部维护三个对象
若是事件类型是Font,那么调用设置字体的事件
复制代码
package mediator;
public class ConcreteMediator extends Mediator {
private Button button;
private Display display;
private Font font;
ConcreteMediator() {
button = new Button(this);
display = new Display(this);
font = new Font(this);
}
@Override
void operation(Colleague event) {
if (event instanceof Font) {
setFontEvent(event);
}
}
private void setFontEvent(Colleague event) {
System.out.println(((Font) event).getFontName());
button.applyButton();
display.reDisplay();
}
}
复制代码
测试代码
复制代码
package mediator;
public class Test {
public static void main(String[] args){
Mediator mediator = new ConcreteMediator();
Font font = new Font(mediator);
font.changeFont();
}
}
复制代码
image_5c171b2b_3c66
上面的示例中,以设置字体为例,当字体变化时,请求“项目经理”安排其余同事协助
“项目经理”operation(Colleague event) 发现是设置字体的事件后,调用对应的事件处理方法,也就是寻找其余同事进行协助
中介者模式将每一个场景中对象之间的协做进行封装
小结
当你须要其余同事协助时,确定不须要项目经理每次都建立具体的同事对象
上面的示例中,在ConcreteMediator的构造方法中建立的各个具体同事的实例(能够理解为项目经理手下有你和你的一帮同事)
你还能够提取出来工厂类用于管理同事类和中介者类
好比经过一个工厂对象管理各个同事实例(若是能够还能够将各个同事都设置为单例 )
而且中介者类做为这个工厂对象的内部类
同事对象的传递能够达到信息的获取与回调
经过给Font加了一个fontName属性,经过打印信息能够看得出来
经过将当时发生事件的对象传递了过来,能够得到事件的更多信息,能够根据信息进一步的进行操做
好比,此处的设置了字体,设置了什么字体?这一进一步的信息就经过参数携带进来
并且,这种方式还能够通知到Font自己
也就是能够在setFontEvent(Colleague event) 事件处理中,调用Font的方法进行结果返回,有回调的韵味
良好的扩展性
若是须要增长一个新的事件处理过程,好比点击取消按钮,还原字体设置,还原预览按钮
只须要在Button新增长一个职责(方法),而后在ConcreteMediator中新增长一种类型的事件处理程序便可
上面的示例中仅仅定义了几个简单的方法,实践中每一个具体同事角色天然不会仅仅只有几个方法
无论有多少方法,均可以经过中介者将他们的协做逻辑进行封装
经过具体的中介者ConcreteMediator的处理方法进行安排
中介者模式能够很好地应用于事件通知的处理
中介者模式时序图
image_5c171b2b_1ce5
当某一个同事对象产生事件后,若是须要其余同事进行协助,他将调用中介者的方法
中介者接到消息后,调用其余同事的方法(安排其余同时干活),而后最终还能够将消息进行返回
与门面模式对比
image_5c171b2b_31b0
门面模式和中介者模式都是经过中间类下降系统的耦合度
可是门面模式是为了子系统提供一个简单一致的接口,客户端透过门面类向子系统发送消息,能够认为消息的发送是单方向的
客户端--->门面--->子系统方法,子系统提供功能给客户端
门面是在外部客户端和内部子系统之间
中介者模式中,则是主要复杂系统内部多个对象之间的相互协做
中介者是各个内部对象同事之间
扩展
任何模式都不是一成不变的,模式结构图只是一种相对比较合适准确的方案
可是涉及到具体的业务中,一切都是可变的
中介者模式也有多种灵活性
若是只有一个中介者对象,显然抽象Mediator角色是能够隐藏的
那么ConcreteMediator就兼顾了这个角色,提供通讯接口给同事角色
中介者模式的本质含义是负责协调系统内部的多个交互的对象,将同事进行解耦
也就是说实现共同的接口并非必须的
而实际上,一个系统中协做的多个对象,极可能是不一样的类型,若是去掉了抽象角色Colleague
那么就能够将任意的有关联的对象组织在一块儿,经过中介者协同工做
并且,也并不意味着必定要中介者持有同事角色,若是合适,直接建立中介者也并不是不能够
虽然上面提到你可使用另外的工厂管理,那也只是一种经常使用用法而已。
只须要记住中介者的本质在于“行为与协做的分离,中介者封装了对象间的协做”
总结
中介者模式将对象的行为和协做进行分离,使对象更加专一于对象的行为,也就是自身的功能
将协做分离出来封装到中介者内部
迪米特法则,不要和陌生人讲话,只与你的朋友通讯,中介者模式是迪米特法则的典型应用
经过引入中介者对象,在同事之间的请求调用中,增长了“项目经理”,若是有事搞不定,须要协助,那么就找他
每一个人只和项目经理对话,也就是仅仅和项目经理这个朋友聊天,其余的同事都不理了  ̄□ ̄||
中介者将网状结构,转换为了星型结构
将多对多的关系,转换为了一对多的关系
因此中介者模式将系统中对象对其余对象的引用数目降到最低,简化了对象之间的交互。
image_5c171b2b_3f1e
中介者模式将各个同事对象之间进行解耦,增长新的中介者或者同事都比较方便,符合开闭原则
MVC模式,也是一种典型的中介者模式,控制器负责视图层和模型层的调度处理,是一个明显的中介者。
中介者模式将同事进行解耦,可是各个Colleague类必须同中介者进行交互
更准确的说,解耦后的关系植入到了中介者自身,若是原来的同事对象间的相互调用很是复杂
那么,这个中介者也极可能很是的复杂难以维护
换言之,同事间的复杂性与耦合性转移到了中介者内部
中介者内部对于各个同事之间的协调代码不太可能复用,因此具体同事类的复用性也是以中介者的不可复用为代价的
若是系统中的各个对象之间存在复杂的引用关系,但愿可以经过中间类将他们进行解耦
或者系统中对象间的引用混乱,交互太多,致使难以进行类的复用
你就能够考虑中介者模式
可是并不意味着任何涉及到多个类交互的地方都用中介者模式,若是本来并不复杂,使用中介者将会增长复杂度
基本前提就是紧密耦合,好比出现了网状结构
原文地址:中介者模式 调停者 Mediator 行为型 设计模式(二十一)设计模式