介绍
顾名思义,中介,在多方之间调和。对应到代码中就是将多个互相依赖的对象通过增加一个中介者来调和。
子系统相互依赖的时候,通过中介者来调用依赖方,这样子系统就不必知道另外的子系统,所有子系统都只知道中介者,而中介者知道所有子系统。
UML
组成结构
- Mediator:抽象中介者角色,定义同事对象到中介者对象的接口,一般以抽象类实现。
- ConcreteMediator:具体中介者角色,实现父类定义的方法,接收同事对象消息,向具体同事对象发出命令
- College:抽象同事角色,定义中介者对象接口,它只知道中介者不知道其他同事。
- ConcreteCollegueA/B:具体同事类角色,每个同事类知道自己和中介者,不知道大范围(其他同事)的目的。
事例
比如咱们要做一个广告功能,大致步骤如下(简易的,可能不完善,理解下思想即可):
- 解析收到新的广告指令
- 解析模块向存储模块咨询是否已有
- 如果没有向下载模块发送下载指令
- 下载模块咨询存储模块存储空间并执行下载
- 下载完成,向播放模块发送进行播放
那么能看到,关于解析模块和下载模块就存在与多个模块交互的情况,互相交互起来是比较复杂的。
那么我们是否也可以简化它呢,比如咱们添加一个中介者改一下结构:
- 指令下发给中介者模块
- 中介者模块发给解析模块解析,解析成功如果需要下载后回发给中介者,请求下载
- 中介者请求存储模块是否存在,存储模块返回不存在则请求下载
- 中介者请求存储模块分配的下载地址
- 中介者模块告诉下载模块进行下载
- 中介者收到下载完整,则通知播放模块进行播放
现在看起来是不是结构清晰多了呢?
那么咱们用代码来实现一下:
- 先创建抽象中介者:提供给同事类调用发送命令方法
/**
* 中介者抽象类
*/
public abstract class AbsMediator {
/**
* 同事对抗发送命令给中介者
* @param colleage 同事对象
* @param info 信息
*/
public abstract void someCMD(AbsColleage colleage, String info);
}
- 再创建抽象同事类:持有中介者
/**
* 抽象同事类
* 引用中介者并提供返回中介者方法
*/
public abstract class AbsColleage {
/**
* 中介者引用
*/
private AbsMediator absMediator;
public AbsColleage(AbsMediator absMediator) {
this.absMediator = absMediator;
}
/**
* 获取中介者
*
* @return
*/
protected AbsMediator getAbsMediator() {
return this.absMediator;
}
}
- 实现同事类:实现同事类对应方法,与相应要对外接收的方法
/**
* 信息解析模块
*/
public class InfoPraseColleage extends AbsColleage {
public static final String REQ_DOWN = "请求下载";
public InfoPraseColleage(AbsMediator absMediator) {
super(absMediator);
}
/**
* 信息解析
*/
public void praseInfo(String info) {
System.out.println("信息解析模块:信息解析");
//请求下载
getAbsMediator().someCMD(this, REQ_DOWN);
}
}
/**
* 存储模块
* 1. 提供广告是否本地存在
* 2. 提供存储文件位置
*/
public class DiskColleage extends AbsColleage {
public DiskColleage(AbsMediator absMediator) {
super(absMediator);
}
/**
* 广告本地是否存在
*
* @param advertiseId 广告id
* @return true 是,false 否
*/
public boolean isExit(String advertiseId) {
System.out.println("存储模块:请求是否存在," + false);
return false;
}
/**
* 获取下载存储位置
*
* @return 存储位置
*/
public String getSavePosition(String advertiseId) {
System.out.println("存储模块:获取下载存储位置");
return "/sdcard/ss/" + advertiseId;
}
}
/**
* 下载模块
* 1. 提供开始下载,下载成功告诉中介者
*/
public class DownColleage extends AbsColleage {
public DownColleage(AbsMediator absMediator) {
super(absMediator);
}
/**
* 下载
*
* @param savePath 存储地址
*/
public void down(String savePath) {
System.out.println("下载模块:开始下载,位置:" + savePath);
//下载成功
getAbsMediator().someCMD(this, savePath);
}
}
/**
* 播放模块
*/
public class PlayColleage extends AbsColleage {
public PlayColleage(AbsMediator absMediator) {
super(absMediator);
}
/**
* 播放
*/
public void play(String path) {
System.out.println("播放模块:开始播放文件,地址:" + path);
}
}
- 实现中介类:实现中介类方法,并提供注册同事类的方法,持有所有同事类,也含有了相关一些业务逻辑
/**
* 实际中介者
*/
public class ConcreteMediator extends AbsMediator {
/**
* 存储模块
*/
private DiskColleage diskColleage;
/**
* 下载模块
*/
private DownColleage downColleage;
/**
* 信息解析模块
*/
private InfoPraseColleage infoPraseColleage;
/**
* 播放模块
*/
private PlayColleage playColleage;
/**
* 假设广告id为111
*/
private static final String adverId = "111";
@Override
public void someCMD(AbsColleage colleage, String info) {
//信息模块解析返回
if (colleage == infoPraseColleage) {
if (InfoPraseColleage.REQ_DOWN.equals(info)) {
//请求下载,
boolean exit = diskColleage.isExit(adverId);
if (!exit) {
//从存储模块申请地址
String savePosition = diskColleage.getSavePosition(adverId);
//开始下载
downColleage.down(savePosition);
}
}
return;
}
//如果是下载模块,下载完成,通知播放模块进行播放
if (colleage == downColleage) {
playColleage.play(info);
}
}
/**
* 开始解析信息
*
* @param info
*/
public void startPraseInfo(String info) {
System.out.println(info);
infoPraseColleage.praseInfo(info);
}
/**
* 设置存储模块
*
* @param diskColleage 存储模块
*/
public void setDiskColleage(DiskColleage diskColleage) {
this.diskColleage = diskColleage;
}
/**
* 下载模块
*
* @param downColleage 下载模块
*/
public void setDownColleage(DownColleage downColleage) {
this.downColleage = downColleage;
}
/**
* 设置信息解析模块
*
* @param infoPraseColleage
*/
public void setInfoPraseColleage(InfoPraseColleage infoPraseColleage) {
this.infoPraseColleage = infoPraseColleage;
}
/**
* 设置播放模块
*
* @param playColleage
*/
public void setPlayColleage(PlayColleage playColleage) {
this.playColleage = playColleage;
}
}
- 测试方法:
public static void main(String[] arg) {
ConcreteMediator concreteMediator = new ConcreteMediator();
//创建信息解析模块
InfoPraseColleage infoPraseColleage = new InfoPraseColleage(concreteMediator);
//创建存储模块
DiskColleage diskColleage = new DiskColleage(concreteMediator);
//下载模块
DownColleage downColleage = new DownColleage(concreteMediator);
//播放模块
PlayColleage playColleage = new PlayColleage(concreteMediator);
//将各个模块注册到中介者中
concreteMediator.setInfoPraseColleage(infoPraseColleage);
concreteMediator.setDiskColleage(diskColleage);
concreteMediator.setDownColleage(downColleage);
concreteMediator.setPlayColleage(playColleage);
concreteMediator.startPraseInfo("收到广告信息,准备开始");
}
- 打印:
收到广告信息,准备开始
信息解析模块:信息解析
存储模块:请求是否存在,false
存储模块:获取下载存储位置
下载模块:开始下载,位置:/sdcard/ss/111
播放模块:开始播放文件,地址:/sdcard/ss/111
以上的写法不那么好,违背了依赖倒置,不过也简单的达到了这个功能的简化结构,通过中介者持有各个同事类模块,来调用同事类模块,同事类模块又只能通过中介者调用其他模块。
实际使用的时候,可以建立消息id机制,同事类往中介者注册监听消息id,中介者只负责将消息转发给其中注册了那条消息id的同事类,这样中介者中就不存在那些业务逻辑了,耦合度也就更低了。
使用场景
- 对象之间操作很多,又依赖复杂时候,可以通过中介者将多对多改为一对多方式的时候
优缺点
优点
- 由网状结构变为了以中介者为中心的星型结构,简化了,模块之间的互相依赖,模块得到了更好的扩展性。即将相互作用由Many to Many 变为One to Many
缺点
- 类变多,改模块可能都会涉及到改中介者类,中介者类代码相对复杂,出问题了调查也比较复杂。
Android中
Keyguard锁屏就是中介者模式的体现,KeyguardViewMediator类中含有AlarmManager、AudioManager、StatusBarManager、PowerManager···,像XXXManager这些就是同事类,都由KeyguardViewMediator来调节
在我们书写代码中,Activity经常也是作为一个中介者在使用。