简介
中介者模式(Mediator Pattern):定义一个对象封装一系列对象的交互,使各对象之间无需显示的相互引用,从而使得其耦合松散,用户可以独立的改变他们之间的交互。
- 系统之间引用关系复杂,耦合过度,需要将两两之间的引用转化为对象和中介者的引用,将系统的网络结构转化成星型结构。
- 这样可以急剧减少关系的数量,保证对象结构上的稳定性,不会因为对象的引入导致大量的修改工作。
- 是迪米特法则的应用。
结构和实现
- 角色包括:
- 抽象中介者:定义接口,声明各个同事之间的交互方法。
- 具体中介者:抽象中介者子类,维持同事对象的引用,实现协调交互的方法。根据需要面向抽象同事类或者具体同事类编程。
- 抽象同事类:定义各个同事类的公有方法,声明抽象方法供子类实现,维持抽象中介者的引用。
- 具体同事类:抽象同事类的子类。
- 中介者模式结构。
- 中介者承担的作用:
- 中转(结构性):各同事通过中介者实现简介调用。
- 协调(行为性):中介者根据自身逻辑处理各个同事的请求。
实例
- 开发客户信息管理窗口,截面组件关系复杂。增加一个客户,需要在客户列表List中增加一项,在组合框ComboBox中增加一项。需要使用中介者模式处理复杂的组件交互。
- 具体中介者,维持各个同事的引用,封装之间的交互。
public class ConcreteMediator extends Mediator {
//维持对各个同事对象的引用
public AddButton addButton;
public List list;
public UserNameTextBox userNameTextBox;
public ComboBox cb;
//封装同事对象之间的交互
public void componentChanged(Component c) {
//单击按钮
if(c == addButton) {
list.update();
cb.update();
userNameTextBox.update();
}
//从列表框选择客户
else if(c == list) {
cb.select();
userNameTextBox.setText();
}
//从组合框选择客户
else if(c == cb) {
cb.select();
userNameTextBox.setText();
}
}
}
- 由于不同组件的方法并不完全相同,中介者没有针对抽象同事类编程,而是在具体中介者中维持具体同事类的引用。
扩展中介者和同事类
- 增加显示当前用户总数的标签。方案如下:
- 增加Label组件,修改原有的具体中介者,增加对Label对象的引用,在componentChanged方法中增加与Label有关的逻辑。
- 增加Label组件,增加一个具体中介者的子类,增加对Label对象的引用,覆盖componentChanged方法在新方法中增加Label相关逻辑。
- 增加Label之后的结构。
- 具体中介者子类。
//新增具体中介者类
public class SubConcreteMediator extends ConcreteMediator {
//增加对Label对象的引用
public Label label;
public void componentChanged(Component c) {
//单击按钮
if(c == addButton) {
list.update();
cb.update();
userNameTextBox.update();
label.update(); //文本标签更新
}
//从列表框选择客户
else if(c == list) {
cb.select();
userNameTextBox.setText();
}
//从组合框选择客户
else if(c == cb) {
cb.select();
userNameTextBox.setText();
}
}
}
- 新增具体同事类,只需要继承抽象同事类,实现其方法。与同事类的交互通过新增具体中介者实现。
- 新增具体中介者,只需要继承抽象中介者类,覆盖其中的方法,增加需要的引用。
优缺点和适用范围
- 优点:
- 简化对象交互。将网状结构转化为星形结构。
- 解耦同事对象。可以独立的改变每一个同时和中介者,更好的符合开闭原则。
- 减少子类生成。将多个对象之间的行为集中于中介者,扩展时只需继承中介者而不是继承各个同事类。
- 缺点:
- 中介者复杂。集中大量同事类之间的交互逻辑,难以维护。
- 适用范围:
- 对象之间引用复杂、混乱,难以理解。
- 一个对象引用其他很多对象,并直接通信,导致对象难以复用。
- 通过一个类封装多个类的交互行为而不愿生成过多子类。
jdk中的应用
- Timer中在schedulexxx方法中通过TaskQueue协调各种TimerTask定时任务,Timer是中介者,TimerTask是同事类。
public class Timer {
private final TaskQueue queue = new TaskQueue();
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);
if (queue.getMin() == task)
queue.notify();
}
}
}
class TaskQueue {
private TimerTask[] queue = new TimerTask[128];
}