今天我们一起学习一下中介者模式。该模式在实际开发中用的可能不是很多,我们主要学习该模式的思想,一般可以用在类似于调度中心、多个模块之间频繁的进行互相调用时,我们就可以考虑使用中介者模式,就比如我们以前在学习 jsp 的时候,不能直接在一个 jsp 中请求另一个 jsp,而是需要在两个 jsp 之间添加一个 controller 控制器,这样就避免了 jsp 的代码臃肿难维护、混乱的问题。
中介者模式: 用一个中介者对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
该定义不难理解,就是说多个对象之间的相互调用会使系统的耦合性增加,我们用一个中介对象来将这些对象的行为进行封装,即由中介者对象来决定什么时候调用哪个对象的哪个方法,而不是这些对象之间的互相调用,这些对象本身就只关心自己应该完成的功能,而不用关心怎样调用别的对象的方法。
举个生活中的例子:我们在看电影《中国机长》的时候,大家应该都有体会到机场塔台的作用,假如机场没有塔台,当刘机长准备降落成都时,他就要关心自己现在是否可以降落,前方是否有跑道可以降落,这些问题都需要他来联系别的机长才能得到答案,在那千钧一发之际,如果这样的话后果可能就会很严重了。所以,这些问题不能够由各个机长来沟通决定。而是由塔台这个中介者来负责调度,机长只需要听从塔台的安排即可。
到这里我们也不难发现中介者模式的优点了,该模式可以把互相耦合的对象进行解耦,使网状结构分散成星型结构。 上图就展示了网状结构,我们可以看到各个模块之间的调用是非常混乱的,各个模块相互依赖,如果一个模块要做出修改,他将影响到其他模块。 该图展示了星型结构,我们可以看出模块之间的依赖关系已经不存在了,每个模块只需要与中间的中介者进行相互依赖即可。
中介者模式有如下几个角色:
Mediator :抽象中介者角色,主要定义一个接口,用于各个同事之间进行互相通信。与同事类相互依赖。
ConcreteMediator:具体的中介者角色,Mediator 的具体实现,将同事之间的相互依赖关系转移到自己中。
Colleague:抽象的同事类,也叫抽象组件,以前互相依赖的各个对象或组件,互相之间以“同事” 称呼。主要定义一些各个具体同事类之间共有的方法。
ConcreteColleague:具体的同事类,Colleague 的具体实现类。当一个同事类想要调用另一个同事的方法的时候,它必须调用中介者的方法,中介者中的方法再去调用其他同事的方法。
由于 ConcreteMediator 与 ConcreteColleague 需要互相通信,所以在 ConcreteMediator 中需要聚合所有的 ConcreteColleague ,所有的 ConcreteColleague 需要聚合 ConcreteMediator 。为了 UML 图看起来简单,我只在 Mediator 和 Colleague 之间使用了 双向关联关系来体现双向的聚合关系。
掌握中介者模式的精髓在于:在同事类自己的方法中,如果要调用其他同事的方法,不管三七二十一,只管调用中介者提供的方法即可,自己唯一要做的是:如何与中介者沟通。中介者中的方法将多个同事的不同方法根据业务需求封装到一起。
那么接下来,我们使用代码来具体描述一下:
Colleague.java:抽象同事类
public abstract class Colleague { private Mediator mediator; public Colleague(Mediator mediator){ this.mediator = mediator; } public Mediator getMediator() { return mediator; } public abstract void methodA(); public abstract void methodB(); }ConcreteColleagueA:具体的抽象类
public class ConcreteColleagueA extends Colleague { public ConcreteColleagueA(Mediator mediator) { super(mediator); mediator.setColleagueA(this); } @Override public void methodA() { System.out.println("ConcreteColleagueA methodA()"); //通过中介者来调用其他同事类的方法 super.getMediator().dealSomething("colleagueA"); } @Override public void methodB() { System.out.println("ConcreteColleagueA methodB()"); } public void methodC() { System.out.println("ConcreteColleagueA methodC()"); } }ConcreteColleagueB.java:
public class ConcreteColleagueB extends Colleague { public ConcreteColleagueB(Mediator mediator) { super(mediator); mediator.setColleagueB(this); } @Override public void methodA() { System.out.println("ConcreteColleagueB methodA()"); } @Override public void methodB() { System.out.println("ConcreteColleagueB methodB()"); super.getMediator().dealSomething("colleagueB"); } }Mediator.java:抽象的中介者类
public abstract class Mediator { private ConcreteColleagueA colleagueA; private ConcreteColleagueB colleagueB; //与同事类进行通信的方法 // public abstract void dealSomething(String message); public ConcreteColleagueA getColleagueA() { return colleagueA; } public void setColleagueA(ConcreteColleagueA colleagueA) { this.colleagueA = colleagueA; } public ConcreteColleagueB getColleagueB() { return colleagueB; } public void setColleagueB(ConcreteColleagueB colleagueB) { this.colleagueB = colleagueB; } }ConcreteMediator.java:具体的中介者类
public class ConcreteMediator extends Mediator { /** * @param message:中介者收到什么样的 消息 就进行相应的处理 * 与同事类中调用该方法是传入的值要匹配 */ @Override public void dealSomething(String message) { if("colleagueA".equals(message)){ super.getColleagueB().methodA(); }else if("colleagueB".equals(message)){ super.getColleagueA().methodC(); super.getColleagueA().methodB(); super.getColleagueA().methodA(); } } }Client.java:测试类:
public class Client { public static void main(String[] args) { Mediator mediator = new ConcreteMediator(); ConcreteColleagueA colleagueA = new ConcreteColleagueA(mediator); ConcreteColleagueB colleagueB = new ConcreteColleagueB(mediator); colleagueA.methodA();//1 //colleagueB.methodB();//2 } }[1] 在测试代码中,注释掉第二行代码(//2 处代码),结果如下: 结果如我们所料,先执行了 ConcreteColleagueA 的 methodA() 方法,再执行 ConcreteColleagueB 的 methodA() 方法。在 ConcreteColleagueA 的 methodA() 通过中介者对象来调用 ConcreteColleagueB 的 methodA() 方法。如果我们不使用中介者模式的话,可能就会出现如下代码:
@Override public void methodA() { System.out.println("ConcreteColleagueA methodA()"); colleagueB.methodA();//对 colleagueB 进行了强依赖 }[2] 在测试代码中,注释掉第一行代码(** //1处代码** ),结果如下: 在调用 ConcreteColleagueB 的 methodB() 方法时,会通过中介者来调用 ConcreteColleagueA 的 methodC()、methodB()、methodA() 方法,当调用到 methodA() 方法时,又会通过中介者调用 ConcreteColleagueB 的 methodA() 方法。所以在使用中介者模式时,一定要注意:不要出现循环调用。
需求:现有两个类 A 和 B ,各自都维护一个数字 num ,无论怎样修改 A 或 B 中的数字,A 中的数字始终都是 B 中的 10 倍。
该示例引用自 23种设计模式(7):中介者模式 。
public abstract class Colleague { private Mediator mediator; private int num ; public Colleague(Mediator mediator){ this.mediator = mediator; } public Mediator getMediator() { return mediator; } public void setMediator(Mediator mediator) { this.mediator = mediator; } public int getNum() { return num; } public void setNum(int num) { this.num = num; } public void changeNum(int num){ this.num = num; } } public class ColleagueA extends Colleague { public ColleagueA(Mediator mediator) { super(mediator); mediator.setColleagueA(this); } @Override public void setNum(int num) { super.setNum(num); //A 设置完自己的数字后,去修改 B 中的数字 //通过中介者来修改 B 中的数字 super.getMediator().AChangeB(); } } public class ColleagueB extends Colleague { public ColleagueB(Mediator mediator) { super(mediator); mediator.setColleagueB(this); } @Override public void setNum(int num) { super.setNum(num); //B 设置完自己的数字后,去修改 A 中的数字 //通过中介者来修改 A 中的数字 super.getMediator().BChangeA(); } } public abstract class Mediator { private ColleagueA colleagueA; private ColleagueB colleagueB; public ColleagueA getColleagueA() { return colleagueA; } public void setColleagueA(ColleagueA colleagueA) { this.colleagueA = colleagueA; } public ColleagueB getColleagueB() { return colleagueB; } public void setColleagueB(ColleagueB colleagueB) { this.colleagueB = colleagueB; } //同事 A 改变 同事 B 中的数字 public abstract void AChangeB(); //同事 B 改变 同事 A 中的数字 public abstract void BChangeA(); } public class ConcreteMediator extends Mediator { @Override public void AChangeB() { //将 A 中的数字 除以 10 赋值给 B int num = super.getColleagueA().getNum(); super.getColleagueB().changeNum( num /10 ); } @Override public void BChangeA() { //将 B 中的数字 乘以 10 赋值给 A int num = super.getColleagueB().getNum(); super.getColleagueA().changeNum( num *10 ); } } public class Client { public static void main(String[] args) { Mediator mediator = new ConcreteMediator(); ColleagueA colleagueA = new ColleagueA(mediator); ColleagueB colleagueB = new ColleagueB(mediator); System.out.println("设置 A 中的数字:100"); colleagueA.setNum(100); System.out.println("A 中的数字:"+colleagueA.getNum()); System.out.println("B 中的数字:"+colleagueB.getNum()); System.out.println("*****************"); System.out.println("设置 B 中的数字:100"); colleagueB.setNum(100); System.out.println("A 中的数字:"+colleagueA.getNum()); System.out.println("B 中的数字:"+colleagueB.getNum()); } }运行结果截图:
首先,我们先总结一下中介者模式的优缺点:
优点:
使得多个同事类之间的相互依赖得到解耦,使得某一个同事类只关心自己的业务,想要使用别的同事的方法时,只需要与中介者通信即可。缺点:
中介者模式容易出现循环调用的问题,编写代码时需要格外小心。中介者对象容易变得很大,其中的代码会变得臃肿。最后,我们来讨论一下 中介者与同事类之间通信的方式:本文通过两个示例代码展示了两种通信方式。第一种:同事类与中介者类通过 message 消息来识别同事类需要中介者完成什么样的功能;第二种:则是直接调用中介者中不同的方法来完成不同的功能。对于这两种方式,个人推荐使用第一种,当有新的同事类增加时,不需要新增方法,而是在 if else 代码中新增一个分支。
示例代码地址