理解设计模式之代理模式、外观模式、中介者模式

代理模式

为其他对象提供一种代理以控制对这个对象的访问。

如何控制对一个对象的访问?计算机科学领域的所有问题都可以通过增加一个间接中间层来解决,增加一层代理类。

1
2
3
4
5
6
7
8
9
10
11
12
13
public class RealSubject {
public void request() {
}
}
public class Proxy {
private RealSubject realSubject;
public void request() {
if (realSubject == null) {
realSubject = new RealSubject();
}
realSubject.request();
}
}

这就是最简单的静态代理。

为什么要去控制一个对象的访问?通过代理对象转发请求给真实对象可以达到:
第一、隐藏这个真实对象。有时候这种情况是需要的,比如简化一个复杂对象的访问,延迟加载真实对象等等
第二、扩展功能。除了直接转发请求,还可以定义一些额外的操作。

为了消除外部对真实对象和代理对象的感知,需要抽象一下RealSubject和Proxy:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public interface ISubject {
void request();
}
public class RealSubject implements ISubject {
@Override
public void request() {
}
}
public class Proxy implements ISubject {
private RealSubject realSubject;
@Override
public void request() {
if (realSubject == null) {
realSubject = new RealSubject();
}
// prerequest() code
realSubject.request();
// postrequest() code
}
}

现在通过ISubject proxy = new Proxy()的proxy对象就能无缝的间接的访问到了RealSubject。UML类图如下:
代理模式UML类图

外观模式

为子系统中的一组接口提供一个一致的界面,Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

外观模式和代理模式形似的地方在于,外观模式是代理模式的宏观版本。代理模式在对象级别代理真实对象,解耦client和真实对象。而外观模式是在系统级别代理子系统,解耦client和子系统,这里的子系统可能还会包括若干子系统。这里还有一个显著的区别就是,外观模式强调的是子系统内的一组接口或者子系统内的一组子系统,它们是1:N的关系。

外观模式UML示意图

这里的SubsystemA,SubsystemB,SubsystemC可大可小,大到包含子系统的大系统,小到子系统的类都可以。
外观模式对降低系统交互的复杂性提出了指导意见,思想还是松耦合、封装等等。

中介者模式

用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。

外观模式解决了对外和client耦合的复杂性,但是如果SubsystemA,SubsystemB,SubsystemC交互在一起也很复杂的话,将面临一个内部复杂性的新问题。
所以,类似于Facade,需要添加一个Mediator来处理这些内部对象的交互,这就是中介者模式。
为了之后的扩展性,把Mediator和交互的对象们Colleague抽象一下:

1
2
3
4
5
6
7
8
9
10
public interface IMediator {
}
// 所有的Colleague不能相互耦合,只依赖IMediator,所以注入IMediator
public abstract class Colleague {
private IMediator mediator;
public void setMediator(IMediator mediator) {
this.mediator = mediator;
}
}

中介者模式的另外一个重点就是:交互!
因为Colleague的实现ColleagueA,ColleagueB,ColleagueC之间没有关系,所以彼此不知道对方的存在,只能把自己状态的变化交给中介者:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public interface IMediator {
void changed(Colleague colleague);
}
public class ColleagueA extends Colleague {
public void actionA() {
mediator.changed(this);
}
}
public class ColleagueB extends Colleague {
public void actionB() {
mediator.changed(this);
}
}
public class ColleagueC extends Colleague {
public void actionC() {
mediator.changed(this);
}
}

让中介者统筹这些Colleague的交互,去通知本来应该被调用的类执行(假设ColleagueA本来要调用ColleagueB,ColleagueB本来要调用ColleagueC):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class Mediator implements IMediator {
// 注入具体Colleague
public ColleagueA colleagueA;
public ColleagueB colleagueB;
public ColleagueC colleagueC;
public void setColleagueA(ColleagueA colleagueA) {
this.colleagueA = colleagueA;
}
public void setColleagueB(ColleagueB colleagueB) {
this.colleagueB = colleagueB;
}
public void setColleagueC(ColleagueC colleagueC) {
this.colleagueC = colleagueC;
}
/**
* 处理交互逻辑的枢纽就在这里
* @param colleague
*/
@Override
public void changed(Colleague colleague) {
if (colleague == colleagueA) {
colleagueB.actionB();
} else if (colleague == colleagueB) {
colleagueC.actionC();
}
}
}

从而实现了colleagueA的actionA()触发,就会连贯的出发ColleagueB的actionB(),再进一步出发ColleagueC的actionC()。
通过调整Mediator的changed()枢纽,就能完成整个一系列对象的各种交互需求了。
一系列对象的多对多交互变成了一对多,复杂度大大降低,UML类图如下:
中介者模式UML类图

另外,中介者模式使用了注册(注入)和通知(changed),这和观察者模式太像了,的确,中介者模式和观察者模式使用的都是事件驱动模型,场景十分类似。但是观察者模式本来就是一对一或者一对多,所以它的交互路径更短,只相当于Mediator和某一个Colleague的交互而已。正因为此,观察者模式在处理一对多或者一对一问题上使用起来更小巧更灵活,中介者模式就不合适了。

小结

中介者模式巧妙的思想。