中介者模式,又叫调停者模式。我看的书里面是叫做调停者的,我个人认为没有“中介者”这个名字容易理解。
中介者模式:在一个庞大系统中,多个类之间需要进行信息的交换,从而引发后续的行为。这个时候为了避免类之间呈网状关联,引入一个中介者用以管理其他类,接收某些类传入的信息,并反映在对应的相应类上,变网状为星状,减少类之间的耦合性。
以上是我对中介者模式的理解,下面我们来看一下在Java语言中,该模式应该如何设计:
- Mediator:中介者,实际上可以作为一个抽象类或接口,用以定义一个中介者需要实现的方法,如记录变更、响应变更、增减Colleague等;
- ConcreteMediator:具体中介者,实现了中介者的方法,明确应该如何进行各项操作;
- Colleague:协同者,一般是一个抽象类或接口,能够返回一个Mediator对象;
- ConcreteColleague:具体协同者,实现了Colleague的方法,并且根据实际情况与Mediator进行交互;
中介者
很简单的一个接口,定义了一个用于反馈的方法:
package com.designpattern;
public interface IMediator {
public void react(AbstractColleague c);
}
协同者
包含一个中介者的实例,为了能够定义构造函数,我将它写成了一个抽象类。请注意,接口不能定义构造函数:
package com.designpattern;
public abstract class AbstractColleague {
protected IMediator mediator;
public AbstractColleague(IMediator m) {
mediator = m;
}
public final IMediator getMediator() {
return mediator;
}
}
具体协同者
我们先来定义两个具体的协同者,他们直接需要进行某些交互的动作,但是由于中介者的存在,他们不必互相了解,只要将自身的变化告诉中介者即可。
ViewColleague
package com.designpattern;
public class ViewColleague extends AbstractColleague {
public ViewColleague(IMediator m) {
super(m);
}
public void mockClick() {
System.out.println("View got a click action.");
getMediator().react(this);
}
public void display(String data) {
System.out.println("View got " + data + " from model.");
}
public void prepareShutdown(){
System.out.println("View knows that: Model gonna shutdown, backup your data.");
}
}
ModelColleague
package com.designpattern;
public class ModelColleague extends AbstractColleague {
public ModelColleague(IMediator m) {
super(m);
}
public String whenClick(){
System.out.println("Model should return some data to view.");
return "data";
}
public void shutdown(){
System.out.println("Model will shutdown in 5 sec.");
getMediator().react(this);
}
}
具体中介者
其实看到我上面两个类的命名之后,大家应该就能猜到我在干什么。我想用中介者模式表现MVC架构,或者也能说我想用MVC来说明中介者模式的作用。
package com.designpattern;
public class ControllerMediator implements IMediator {
private ViewColleague viewColleague;
private ModelColleague modelColleague;
public ControllerMediator setView(ViewColleague v){
viewColleague = v;
return this;
}
public ControllerMediator setModel(ModelColleague m){
modelColleague = m;
return this;
}
@Override
public void react(AbstractColleague c) {
if (c instanceof ViewColleague) {
String data = modelColleague.whenClick();
viewColleague.display(data);
} else if (c instanceof ModelColleague) {
viewColleague.prepareShutdown();
} else {
System.out.println("Not Supported Class: " + c.getClass().getName());
// you can also :
//throw new NotSupportedException();
}
}
}
大家可以看到,在中介者中保存了两个Colleague对象,如果说我们现在展示的不是MVC,而是MVVM框架,那么就会有更多的实例保存在终结者中。这个是无法避免的,中介者必须拥有所有的协同者。
好的,然后我们来写一个main方法驱动整个代码:
public static void main(String[] args){
ControllerMediator controller = new ControllerMediator();
ViewColleague view = new ViewColleague(controller);
ModelColleague model = new ModelColleague(controller);
controller.setView(view).setModel(model);
//view change:
view.mockClick();
//model change:
model.shutdown();
}
我们先将协同者和中介者进行关联,然后尝试模拟View的变更以及Model的变更。执行main方法,可以看到输出结果如下:
View got a click action.
Model should return some data to view.
View got data from model.
Model will shutdown in 5 sec.
View knows that: Model gonna shutdown, backup your data.
View出现了状态的改变,但它不需要知道有谁需要因此而做什么事情,只需要告诉中介者即可,Model亦然。两者存在着数据和信息的交换,但是又不知道彼此,一个中介者将两者完美地解耦了。
中介者模式和观察者模式
其实在思考和编写这个例子的时候,我一直在想这两个模式的关系。两者虽然很相似,但是理解之后还是能发现有很大区别的:
- 观察者模式中,变更发生在被观察者,即中心节点;中介者模式中,变更发生在某一个协同类,而非中心节点-中介类;
- 观察者模式中,针对变更的行为由观察者实现;中介者模式中,虽然会使用到协同类的某些方法,但是具体行为顺序和参数则由中介者来确定;
- 观察者模式中,由于观察者实现了同一接口,在被观察者看来是一组相同的对象;在中介者模式中,每个协同类虽然一般都会源自某个类或接口,但实际上各司其职,是不同的对象,有着自身独特的方法;
- 观察者模式中,观察者的增加不会影响被观察者的行为;中介者模式中,协同类的增加,势必会影响中介者的行为;
以上四点,是我自己能想到的一些区别,其中关于第四点我想多说一点,其实它也就是中介者模式存在的弊端。
牺牲小我,完成大我
中介者模式能够让协同类之间的耦合性消失,但又引出了新的问题:它自己。由于集所有协同类与一身,并且负责担当类之间的调度员和粘合剂,使得中介者自身改动频繁,且难于维护。降低了系统耦合,却将所有复杂度和内聚性融于自身。
因此,合理选择中介者的使用场景和使用粒度非常重要,不需要为了使用设计模式而使用。要让中介者的牺牲是值得的才行。