一. 定义:
用一个中介对象来封装一系列对象交互。中介者使得各对象不需要显式地相互引用,从而使其松散耦合,而且可以独立的改变它们之间的交互。
假设电脑没有主板,那么各个配件之间就必须自行相互交互,以互相传送数据,理论上基本上每个配件相互之间都存在交互数据的可能。
现在有了主板,各个配件的交互完全通过主板来完成,每个配件都只需要和主板交互,而主板知道如何和所有的配件打交道,那就简单多了。
二. 结构和说明:
Mediator:中介者接口,在里面定义各个同事之间交互需要的方法,可以是公共的通讯方法,比如changed方法,大家都用
public interface Mediator { /** * 同事对象在自身改变的时候来通知中介者的方法, * 让中介者去负责相应的与其他同事对象的交互 * @param colleague 同事对象自身,好让中介者对象通过对象实例 * 去获取同事对象的状态 */ public void changed(Colleague colleague); }ConcreteMediator:具体中介者实现对象,它需要了解并维护各个同事对象,并负责具体的协调各同事对象的交互关系。
public class ConcreteMediator implements Mediator { /** * 持有并维护同事A */ private ConcreteColleagueA colleagueA; /** * 持有并维护同事B */ private ConcreteColleagueB colleagueB; /** * 设置中介者需要了解并维护的同事A对象 * @param colleague 同事A对象 */ public void setConcreteColleagueA(ConcreteColleagueA colleague) { colleagueA = colleague; } /** * 设置中介者需要了解并维护的同事B对象 * @param colleague 同事B对象 */ public void setConcreteColleagueB(ConcreteColleagueB colleague) { colleagueB = colleague; } public void changed(Colleague colleague) { //某个同事类发生了变化,通常需要与其他同事交互 //具体协调相应的同事对象来实现协作行为 } }Colleague:同事类的定义,通常实现成为抽象类,主要负责约束同事对象的类型,并实现一些具体同事类之间的公共功能,比如:每个具体同事类都应该知道中介者对象,也就是具体同事类都会持有中介者对象,就可以定义到这个类里面。
public abstract class Colleague { /** * 持有中介者对象,每一个同事类都知道它的中介者对象 */ private Mediator mediator; /** * 构造方法,传入中介者对象 * @param mediator 中介者对象 */ public Colleague(Mediator mediator) { this.mediator = mediator; } /** * 获取当前同事类对应的中介者对象 * @return 对应的中介者对象 */ public Mediator getMediator() { return mediator; } }
ConcreteColleague:具体的同事类,实现自己的业务,在需要与其他同事通讯的时候,就与持有的中介者通信,中介者会负责与其他的同事交互。
public class ConcreteColleagueA extends Colleague { public ConcreteColleagueA(Mediator mediator) { super(mediator); } /** * 示意方法,执行某些业务功能 */ public void someOperation() { //在需要跟其他同事通信的时候,通知中介者对象 getMediator().changed(this); } }
三. 现实例子:
现在我们需要用电脑来看电影,假设有如下的交互过程:
1. 首先是光驱要读取光盘上的数据,然后告诉主板它的状态改变了
2. 主板去得到光驱的数据,并把这些数据交给cpu进行分析处理
3. cpu处理完后,把数据分成了视频数据和音频数据,通知主板,它处理完了
4. 主板去得到cpu处理过后的数据,分别把数据交给显卡和声卡,去显示出视频和发出声音
① 中介者对象的接口:
public interface Mediator { /** * 同事对象在自身改变的时候来通知中介者的方法, * 让中介者去负责相应的与其他同事对象的交互 * @param colleague 同事对象自身,好让中介者对象通过对象实例去获取同事对象的状态 */ public void changed(Colleague colleague); }②. 主板类,实现中介者接口
public class MainBoard implements Mediator{ // 需要知道要交互的同事类——光驱类 private CDDriver cdDriver = null; // 需要知道要交互的同事类——CPU类 private CPU cpu = null; // 需要知道要交互的同事类——显卡类 private VideoCard videoCard = null; // 需要知道要交互的同事类——声卡类 private SoundCard soundCard = null; public void setCdDriver(CDDriver cdDriver) { this.cdDriver = cdDriver; } public void setCpu(CPU cpu) { this.cpu = cpu; } public void setVideoCard(VideoCard videoCard) { this.videoCard = videoCard; } public void setSoundCard(SoundCard soundCard) { this.soundCard = soundCard; } public void changed(Colleague colleague) { if(colleague == cdDriver){ //表示光驱读取数据了 this.opeCDDriverReadData((CDDriver)colleague); }else if(colleague == cpu){ //表示CPU处理完了 this.opeCPU((CPU)colleague); } } /** * 处理光驱读取数据过后与其他对象的交互 * @param cd 光驱同事对象 */ private void opeCDDriverReadData(CDDriver cd){ //1:先获取光驱读取的数据 String data = cd.getData(); //2:把这些数据传递给CPU进行处理 this.cpu.executeData(data); } /** * 处理CPU处理完数据后与其他对象的交互 * @param cpu CPU同事类 */ private void opeCPU(CPU cpu){ //1:先获取CPU处理过后的数据 String videoData = cpu.getVideoData(); String soundData = cpu.getSoundData(); //2:把这些数据传递给显卡和声卡展示出来 this.videoCard.showData(videoData); this.soundCard.soundData(soundData); } }③ 同事类的抽象父类
public abstract class Colleague { // 持有中介者对象,每一个同事类都知道它的中介者对象 private Mediator mediator; /** * 构造方法,传入中介者对象 * @param mediator 中介者对象 */ public Colleague(Mediator mediator) { this.mediator = mediator; } /** * 获取当前同事类对应的中介者对象 * @return 对应的中介者对象 */ public Mediator getMediator() { return mediator; } }④ cpu类,继承同事类
public class CPU extends Colleague{ // 解出来的视频数据 private String videoData = ""; // 分解出来的声音数据 private String soundData = ""; public CPU(Mediator mediator) { super(mediator); } /** * 获取分解出来的视频数据 * @return 分解出来的视频数据 */ public String getVideoData() { return videoData; } /** * 获取分解出来的声音数据 * @return 分解出来的声音数据 */ public String getSoundData() { return soundData; } /** * 处理数据,把数据分成音频和视频的数据 * @param data 被处理的数据 */ public void executeData(String data){ // 把数据分解开,前面的是视频数据,后面的是音频数据 String [] ss = data.split(","); this.videoData = ss[0]; this.soundData = ss[1]; // 通知主板,CPU的工作完成 this.getMediator().changed(this); } }⑤ 光驱类,继承同事类
public class CDDriver extends Colleague{ // 光驱读取出来的数据 private String data = ""; public CDDriver(Mediator mediator) { super(mediator); } /** * 获取光驱读取出来的数据 * @return 光驱读取出来的数据 */ public String getData(){ return this.data; } /** * 读取光盘 */ public void readCD(){ //逗号前是视频显示的数据,逗号后是声音 this.data = "设计模式,值得好好研究"; //通知主板,自己的状态发生了改变 this.getMediator().changed(this); } }⑥ 光驱类,继承同事类
public class SoundCard extends Colleague{ public SoundCard(Mediator mediator) { super(mediator); } /** * 按照声频数据发出声音 * @param data 发出声音的数据 */ public void soundData(String data){ System.out.println("画外音:"+data); } }⑦ 显卡类,继承同事类
public class VideoCard extends Colleague{ public VideoCard(Mediator mediator) { super(mediator); } /** * 显示视频数据 * @param data 被显示的数据 */ public void showData(String data){ System.out.println("您正观看的是:"+data); } }⑧ 客户端:
public class Client { public static void main(String[] args) { //1:创建中介者——主板对象 MainBoard mainBoard = new MainBoard(); //2:创建同事类 CDDriver cd = new CDDriver(mainBoard); CPU cpu = new CPU(mainBoard); VideoCard vc = new VideoCard(mainBoard); SoundCard sc = new SoundCard(mainBoard); //3:让中介者知道所有的同事 mainBoard.setCdDriver(cd); mainBoard.setCpu(cpu); mainBoard.setVideoCard(vc); mainBoard.setSoundCard(sc); //4:开始看电影,把光盘放入光驱,光驱开始读盘 cd.readCD(); } }
四. 认识中介者模式:
中介者模式就是封装对象之间的交互,如果一个对象的操作会引起其他相关对象的变化或连带操作,而这个对象又不希望自己来处理这些关系,那么就可以找中介者。最后对象之间就完全分离了,谁都不直接跟其他对象交互,相互的关系全部集中到中介者对象里面,所有的对象就只跟中介者对象进行通信,相互之间不再有联系。
所有对象之间的交互都封装在中介者,无形中还得到另外一个好处,就是能够集中的控制这些对象的交互关系,这样有什么变化的时候,修改起来很方便。
五. 理解中介者模式:
标准的中介者模式在实际使用中的困难:
1. 是否有必要为同事对象定义一个公共的父类?
java是单继承,为了使用中介者模式,就要强迫这些同事对象继承一个父类,这不好,况且这个父类并没有什么特别的公共方法,也就是说通过继承它并没有得到什么好处。
2. 同事类有必要持有中介者对象吗?
同事类需要知道中介者对象,以便当它们发生改变的时候,能够通知中介者对象,但是是否需要作为属性,并通过构造方法传入这么强的依赖关系吗?
其实有更简单的方式通知中介对象,比如把中介对象做成单例,直接在同事类的方法里面去调用中介者对象。
3. 是否需要中介者接口?在实际开发中,很常见的情况是不需要中介者接口的,而且中介者对象也不需要创建很多个实例,因为中介者是用来封装和处理同事对象的关系的,它一般是没有状态需要维护的,因此中介者通常可以实现成单例。
4. 中介者对象是否需要持有所有的同事?
虽说中介者对象需要知道所有的同事类,这样中介者才能与他们交互,但是是否需要作为属性这么强烈的依赖关系呢?其实我们可以在中介者处理的方法里面去创建,或者获取,或者从参数传入需要的同事对象。
5. 中介者对象只是提供一个公共的方法来接受同事对象的通知吗?
在实际开发中,通常会提供具体的业务通知方法,这样就不用再去判断到底是什么对象,具体是什么业务了。
六. 中介者模式优缺点:
1. 松散耦合
2. 集中控制交互
3. 过度集中化
七. 何时选用中介者模式:
1.如果一组对象之间的通信方式比较复杂,导致相互依赖,结构混乱,可以采用中介者模式,把这些对象相互的交互管理起来,各个对象只需要和中介者交互,从而使得各个对象松散耦合,结构也更清晰易懂。
2.如果一个对象引用很多的对象,并直接和这些对象交互,导致难以复用该对象。可以采用中介者模式,把这个对象跟其他对象的交互封装到中介者对象里面,这个对象就只需要和中介者对象交互就可以了。