一. 定义:
用一个中介对象来封装一系列对象交互。中介者使得各对象不需要显式地相互引用,从而使其松散耦合,而且可以独立的改变它们之间的交互。
假设电脑没有主板,那么各个配件之间就必须自行相互交互,以互相传送数据,理论上基本上每个配件相互之间都存在交互数据的可能。
现在有了主板,各个配件的交互完全通过主板来完成,每个配件都只需要和主板交互,而主板知道如何和所有的配件打交道,那就简单多了。
二. 结构和说明:
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.如果一个对象引用很多的对象,并直接和这些对象交互,导致难以复用该对象。可以采用中介者模式,把这个对象跟其他对象的交互封装到中介者对象里面,这个对象就只需要和中介者对象交互就可以了。