中介模式
“中介”
- 租房 --“中介”
- 找工作---“人才交流中心”
- 在 MVC 框架中--“控制器(C)”
定义
定义一个中介对象来封装一系列对象之间的交互,使原有对象之间的耦合松散,且可以独立地改变它们之间的交互。大大降低对象之间的耦合性,提高系统的灵活性。
应用场景
- 系统中对象之间存在复杂的引用关系,产生的相互依赖关系结构混乱且难以理解。
- 想通过一个中间类来封装多个类的行为
举例
背景: 六个房东都正在出租自己房屋,房屋都各有特点,租客A来租房,看房东一不满意,房东一让他去看其他房间,开始联系房东二、三、四、五...
变动: 如果房东二在某天发生了变化,房子已经租出去了,这个时候他需要告知其他五位房东,不用给他介绍租客了
问题: 如果中间一个人发生变化,则需要改动其他五个人
优化: 引入中介,所有的房屋交给中介,租客A不满意,中介带她看其他房,如果中间有一个房被出租了,则中介只需要和对应的房东联系即可,其他五位房东不会受影响
不使用中介模式:
使用中介模式:
- 世界上存在着各种各样的数据库,不同数据库有各自的应用场景,对于同一份数据,最开始可能使用关系型数据库(如MySQL)进行存储查询,使用Redis作为缓存数据库,当数据量较大时使用MySQL进行查询可能较慢,所以需要将数据同步到Elasticsearch或者列式数据库如Hbase中进行大数据查询。个数据源直接同步数据到目标端数据库的,如果数据库有 N 个,那么最多可能的同步作业将达到 N * N 个,当修改了其中一个数据库的某些配置,可能需要修改另外的 N - 1 个数据库的同步作业。DataX 其实相当于一个中介,从数据源读取数据,写入到目标端,数据源不再需要维护到目标端的同步作业,只需要与 DataX 通信即可。DataX 体现了中介者模式的思想
“网状”---》“星状”
引入一个中介者对象,各个同事对象只跟中介者对象打交道
两个职责:
中转作用:通过中介者提供的中转作用,各个同事对象就不再需要显式引用其他同事,当需要和其他同事进行通信时,可通过中介者来实现间接调用。该中转作用属于中介者在结构上的支持。
协调作用:中介者可以更进一步的对同事之间的关系进行封装,同事可以一致的和中介者进行交互,而不需要指明中介者需要具体怎么做,中介者根据封装在自身内部的协调逻辑,对同事的请求进行进一步处理,将同事成员之间的关系行为进行分离和封装。
代码实现
- 抽象中介者(Mediator)角色:它是中介者的接口,提供了同事对象注册与转发同事对象信息的抽象方法。
- 具体中介者(Concrete Mediator)角色:实现中介者接口,定义一个 List 来管理同事对象,协调各个同事角色之间的交互关系,因此它依赖于同事角色。
- 抽象同事类(Colleague)角色:定义同事类的接口,保存中介者对象,提供同事对象交互的抽象方法,实现所有相互影响的同事类的公共功能。
- 具体同事类(Concrete Colleague)角色:是抽象同事类的实现者,当需要与其他同事对象交互时,由中介者对象负责后续的交互。
/**
* Description: <br> 抽象中介者
*/
public abstract class Mediator {
//注册
public abstract void register(Colleague colleague);
//转发
public abstract void relay(Colleague cl);
}
/**
* Description: <br> 抽象同事类
*
* @author wangfeng@tsintergy.com
* @create 2021/12/12
* @since 1.0.0
*/
public abstract class Colleague {
protected Mediator mediator;
public void setMedium(Mediator mediator) {
this.mediator = mediator;
}
/**
* 接收消息
*/
public abstract void receive();
/**
* 发送消息
*/
public abstract void send();
}
/**
* Description: <br> 具体中介
*
* @author wangfeng@tsintergy.com
* @create 2021/12/12
* @since 1.0.0
*/
public class ConcreteMediator extends Mediator {
private List<Colleague> colleagues = new ArrayList<Colleague>();
@Override
public void register(Colleague colleague) {
if (!colleagues.contains(colleague)) {
colleagues.add(colleague);
colleague.setMedium(this);
}
}
@Override
public void relay(Colleague cl) {
for (Colleague ob : colleagues) {
if (!ob.equals(cl)) {
((Colleague) ob).receive();
}
}
}
}
/**
* Description: <br> 具体同事类1
*
* @author wangfeng@tsintergy.com
* @create 2021/12/12
* @since 1.0.0
*/
public class ConcreteColleague1 extends Colleague {
@Override
public void receive() {
System.out.println("具体同事类1收到请求。");
}
@Override
public void send() {
System.out.println("具体同事类1发出请求。");
mediator.relay(this); //请中介者转发
}
}
/**
* Description: <br> 具体同事类2
*
* @author wangfeng@tsintergy.com
* @create 2021/12/12
* @since 1.0.0
*/
public class ConcreteColleague2 extends Colleague {
@Override
public void receive() {
System.out.println("具体同事类2收到请求。");
}
@Override
public void send() {
System.out.println("具体同事类2发出请求。");
mediator.relay(this); //请中介者转发
}
}
public static void main(String[] args) {
Mediator md = new ConcreteMediator();
Colleague c1 = new ConcreteColleague1();
Colleague c2 = new ConcreteColleague2();
md.register(c1);
md.register(c2);
c1.send();
System.out.println("-------------");
c2.send();
}
具体同事类1发出请求。
具体同事类2收到请求。
-------------
具体同事类2发出请求。
具体同事类1收到请求。
优点
1、松散耦合、将多个对象之间的联系紧耦合封装到中介对象中,做到松耦合。不会导致一动牵全身。
2、将多个对象之间的交互联系集中在中介对象中。发送变化仅需修改中介对象即可、提供系统的灵活性、使同事对象独立而易于复用。
3、符合迪米特原则。就是说一个对象应当对其他对象有尽可能少的了解。减少各个对象之间的了解。
缺点
如果各个同事间的交互非常多并且复杂情况下,都交给中介者会导致中介者变得十分复杂,不易维护和管理。
源码中的中介者模式
- Timer#schedule
Timer:
- Timer类中多个scheduler()的重载方法
- 所有的scheduler()的方法内部都调用了私有方法sched()
- schde()方法源码---安排指定的定时器任务在指定的时间执行
private void sched(TimerTask task, long time, long period) {
if (time < 0)
throw new IllegalArgumentException("Illegal execution time.");
// Constrain value of period sufficiently to prevent numeric
// overflow while still being effectively infinitely large.
if (Math.abs(period) > (Long.MAX_VALUE >> 1))
period >>= 1;
// 获取任务队列的锁
synchronized(queue) {
/ /如果定时调度线程已经终止了,则抛出异常结束
if (!thread.newTasksMayBeScheduled)
throw new IllegalStateException("Timer already cancelled.");
synchronized(task.lock) {
判断线程的状态
if (task.state != TimerTask.VIRGIN)
throw new IllegalStateException(
"Task already scheduled or cancelled");
task.nextExecutionTime = time;
task.period = period;
task.state = TimerTask.SCHEDULED;
}
// 将任务加入任务队列
queue.add(task);
// 如果当前加入的任务是需要第一个被执行的(也就是他的下一次执行时间离现在最近)// 则唤醒等待queue的线程
if (queue.getMin() == task)
queue.notify();
}
}
import java.util.TimerTask;
public class TimerThread extends Thread {
boolean newTasksMayBeScheduled = true;
/**
* 任务队列的引用
*/
private TaskQueue queue;
TimerThread(TaskQueue queue) {
this.queue = queue;
}
public void run() {
try {
mainLoop();
} finally {
// 任务结束,例如Timer 执行了cancel
synchronized(queue) {
newTasksMayBeScheduled = false;
queue.clear(); //清除废弃对象的引用
}
}
}
/**
*
*/
private void mainLoop() {
while (true) {
try {
TimerTask task;
boolean taskFired;
synchronized(queue) {
// 如果暂时没有任务,并且TimerThread线程没有被取消就等待
while (queue.isEmpty() && newTasksMayBeScheduled)
queue.wait();
if (queue.isEmpty())
break;
long currentTime, executionTime;
task = queue.getMin();
synchronized(task.lock) {
if (task.state == TimerTask.CANCELLED) {
queue.removeMin();
continue;
}
currentTime = System.currentTimeMillis();
executionTime = task.nextExecutionTime;//下一次执行的时间
//如果下一次执行时间小于等于当前时间说明可以执行了
if (taskFired = (executionTime<=currentTime)) {
if (task.period == 0) {
queue.removeMin();
task.state = TimerTask.EXECUTED;
}
else {
queue.rescheduleMin(
task.period<0 ? currentTime - task.period
: executionTime + task.period);
}
}
}
if (!taskFired)
queue.wait(executionTime - currentTime);
}
if (taskFired)
task.run();//只是调用的run方法没有启用新的线程
} catch(InterruptedException e) {
}
}
}
}
- Timer 这个中介者的功能就是定时调度我们写的各种任务,将任务添加到 TaskQueue 任务队列中,给 TimerThread 执行,让任务与执行线程解耦
- Timer 是中介者,TimerTask是抽象同事类,我们自己写的任务则是具体同事类
回顾相似模式
外观模式
代理模式
三者区别
中介者模式:注重的是将多对多的关系处理封装到一对多、通过一个中介者对象对多个对象之间的依赖进行解耦
外观模式: 外观模式注重的是简化接口,简化组件系统与外部程序的依赖关系。提供一个更高层次的统一接口,使子系统更加简便的使用
代理模式: 注重的是对其他对象的访问控制、倾向于的是一对一的关系处理