1.应用场景
世界上存在着各种各样的数据库,不同数据库有各自的应用场景,对于同一份数据,最开始可能使用关系型数据库(如MySQL)进行存储查询,使用Redis作为缓存数据库,当数据量较大时使用MySQL进行查询可能较慢,所以需要将数据同步到Elasticsearch或者列式数据库如Hbase中进行大数据查询。如何设计数据同步方案是一个重要的问题。数据源众多,目标端也众多,设计得不好可能 “牵一发而动全身”。
如果我们这样设计:每个数据源直接同步数据到目标端数据库的,如果数据库有 N 个,那么最多可能的同步作业将达到 N * N 个,当修改了其中一个数据库的某些配置,可能需要修改另外的 N - 1 个数据库的同步作业。
现在介绍另一种方案,DataX 是阿里巴巴集团内被广泛使用的离线数据同步工具/平台,实现包括 MySQL、Oracle、SqlServer、Postgre、HDFS、Hive、ADS、HBase、TableStore(OTS)、MaxCompute(ODPS)、DRDS 等各种异构数据源之间高效的数据同步功能。
DataX 其实相当于一个中介,从数据源读取数据,写入到目标端,数据源不再需要维护到目标端的同步作业,只需要与 DataX 通信即可。DataX 体现了中介者模式的思想。
2. 概念
中介者模式(Mediator Pattern):用一个中介对象(中介者)来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。中介者模式又称为调停者模式,它是一种对象行为型模式。
3. Class Diagram
- Mediator(抽象中介者):它定义一个接口,该接口用于与各同事对象之间进行通信。
- ConcreteMediator(具体中介者):它是抽象中介者的子类,通过协调各个同事对象来实现协作行为,它维持了对各个同事对象的引用。
- Colleague(抽象同事类):它定义各个同事类公有的方法,并声明了一些抽象方法来供子类实现,同时它维持了一个对抽象中介者类的引用,其子类可以通过该引用来与中介者通信。
- ConcreteColleague(具体同事类):它是抽象同事类的子类;每一个同事对象在需要和其他同事对象通信时,先与中介者通信,通过中介者来间接完成与其他同事类的通信;在具体同事类中实现了在抽象同事类中声明的抽象方法。
4. Implementation
Alarm(闹钟)、CoffeePot(咖啡壶)、Calendar(日历)、Sprinkler(喷头)是一组相关的对象,在某个对象的事件产生时需要去操作其它对象,形成了下面这种依赖结构:
使用中介者模式可以将复杂的依赖结构变成星形结构:
public abstract class Colleague {
public abstract void onEvent(Mediator mediator);
}
public class Alarm extends Colleague {
@Override
public void onEvent(Mediator mediator) {
mediator.doEvent("alarm");
}
public void doAlarm() {
System.out.println("doAlarm()");
}
}
public class CoffeePot extends Colleague {
@Override
public void onEvent(Mediator mediator) {
mediator.doEvent("coffeePot");
}
public void doCoffeePot() {
System.out.println("doCoffeePot()");
}
}
public class Calender extends Colleague {
@Override
public void onEvent(Mediator mediator) {
mediator.doEvent("calender");
}
public void doCalender() {
System.out.println("doCalender()");
}
}
public class Sprinkler extends Colleague {
@Override
public void onEvent(Mediator mediator) {
mediator.doEvent("sprinkler");
}
public void doSprinkler() {
System.out.println("doSprinkler()");
}
}
public abstract class Mediator {
public abstract void doEvent(String eventType);
}
public class ConcreteMediator extends Mediator {
private Alarm alarm;
private CoffeePot coffeePot;
private Calender calender;
private Sprinkler sprinkler;
public ConcreteMediator(Alarm alarm, CoffeePot coffeePot, Calender calender, Sprinkler sprinkler) {
this.alarm = alarm;
this.coffeePot = coffeePot;
this.calender = calender;
this.sprinkler = sprinkler;
}
@Override
public void doEvent(String eventType) {
switch (eventType) {
case "alarm":
doAlarmEvent();
break;
case "coffeePot":
doCoffeePotEvent();
break;
case "calender":
doCalenderEvent();
break;
default:
doSprinklerEvent();
}
}
public void doAlarmEvent() {
alarm.doAlarm();
coffeePot.doCoffeePot();
calender.doCalender();
sprinkler.doSprinkler();
}
public void doCoffeePotEvent() {
// ...
}
public void doCalenderEvent() {
// ...
}
public void doSprinklerEvent() {
// ...
}
}
public class Client {
public static void main(String[] args) {
Alarm alarm = new Alarm();
CoffeePot coffeePot = new CoffeePot();
Calender calender = new Calender();
Sprinkler sprinkler = new Sprinkler();
Mediator mediator = new ConcreteMediator(alarm, coffeePot, calender, sprinkler);
// 闹钟事件到达,调用中介者就可以操作相关对象
alarm.onEvent(mediator);
}
}
运行结果:
doAlarm()
doCoffeePot()
doCalender()
doSprinkler()
5. 优点和缺点
5.1 优点
- 通过将对象彼此解耦,可以增加对象的复用性。
- 通过将控制逻辑集中,可以简化系统维护。
- 可以让对象之间所传递的消息便得简单而且大幅减少。
5.2 用途和缺点
- 中介者常常用来协调相关GUI组件
- 中介者模式的缺点时,如果设计不当,中介者模式会变得过于复杂。
6. JDK
- All scheduleXXX() methods of java.util.Timer
- java.util.concurrent.Executor#execute()
- submit() and invokeXXX() methods of java.util.concurrent.ExecutorService
- scheduleXXX() methods of java.util.concurrent.ScheduledExecutorService
- java.lang.reflect.Method#invoke()