中介模式(mediator pattern)是一种“平台式”或“管家式”委派方式。
中介模式中,中介是各个希望相互交互的参与者的共同且唯一代理,它完成消息的转发或处理。
平台式中介
现实生活中有集市、股票交易所、婚姻介绍所、房屋中介等形形色色的中介(mediator)组织,它们有效地将参与者(participator)的两两交互转化为参与者与中介的交互。但是,这些中介正好是GoF中介模式的错误例子。网上有许多人用QQ为例引入中介模式,显然没有从代码的角度分析,纯属想当然。
这里说一个现实的场景。一场Party,有男生/Boy、女生/Girl参加,每个人都可以与其他对象交互。请问,实现这一场景的代码有多复杂?只有当你真正写出代码时,你会发现10万个对象的彼此交谈,也不过两个类的代码要写。
例程 5 9 不需要中介 package delegate.mediator.party; public abstract class Actor{ String name; public abstract void send(String message,Actor to); public abstract void receive(String message,Actor from); } package delegate.mediator.party; import static tool.Print.*; public class Boy extends Actor{ private int count; public Boy(String name){ this.name =name; } @Override public void send(String message,Actor to){ pln(this.name+" To "+to.name+":\""+message+"\""); to.receive(message,this); } @Override public void receive(String message,Actor from){ char c = (char)(Math.random()*4+'a'); if(c!='b'){ String msg = message+" "+c; send(msg,from); } } } package delegate.mediator.party; public class Party{ public static void main(){ Actor[] alist = new Actor[]{ new Boy("B1"),new Boy("B2"),new Boy("B3"),new Boy("B4"), new Girl("G1"),new Girl("G2"),new Girl("G3") }; for(int i=0;i<alist.length;i++){ int rand= (int)(Math.random()*alist.length); alist[i].send("hi",alist[rand]); } } }
测试代码中,每一个Party参与者发起一次对话,对话的对象是随机的。对话的内容为随机字符,对话必须持续直到随机字符为b(bay)才结束。某一次运行结果(”//结束“不是程序的输出):
B1 To B4:"hi"
B4 To B1:"hi d"
B1 To B4:"hi d d"
B4 To B1:"hi d d a"
B1 To B4:"hi d d a a" //结束
B2 To B3:"hi" //结束
B3 To G2:"hi"//结束
B4 To B1:"hi"//结束
G1 To G2:[hi]
G2 To G1:[hi d]
G1 To G2:[hi d d]
G2 To G1:[hi d d d]
G1 To G2:[hi d d d a]
G2 To G1:[hi d d d a d]
G1 To G2:[hi d d d a d c]
G2 To G1:[hi d d d a d c a]
G1 To G2:[hi d d d a d c a a]
G2 To G1:[hi d d d a d c a a a]
G1 To G2:[hi d d d a d c a a a a]
G2 To G1:[hi d d d a d c a a a a c] //居然聊了这么多 //结束
G2 To B2:[hi]
B2 To G2:"hi c"
G2 To B2:[hi c a]
B2 To G2:"hi c a a"
G2 To B2:[hi c a a a]
B2 To G2:"hi c a a a c"
G2 To B2:[hi c a a a c a]
G3 To G1:[hi]
在讨论中介模式时,许多人看到对象的交互呈现网状结构,几乎每个对象都需要与其他对象发生相互作用。[GoF]中介者模式:“定义一个中介对象来封装系列对象之间的交互。中介者使各个对象不需要显示地相互引用,从而使其耦合性松散,而且可以独立地改变他们之间的交互”。事实上,上述代码告诉了一个事实,这里不需要中介模式:
①各对象之间并不需要“显式”持有彼此的引用。Boy和Girl是Actor的子类,即使Party中出现了Boy和Girl等具体类型,Boy和Girl的类体中依赖Actor即可。②多个类(Boy、Girl)的大量对象的交互,并不一定会导致类之间的过度耦合。Boy和Girl之间就不存在任何耦合。类似的,让家长、老师等参加Party,只要他们是Actor,彼此之间不存在任何耦合。
所以,上面的例子并不是我们需要中介(这里不是中介模式)的原因。
然而,如果有一个对象需要找一个女朋友,他一个个地与每一个Girl对象交互,就显得非常低效和不现实。此时,他需要一个能够广播的“平台”。平台式的中介中,不管参与者如何形形色色,中介的作用如同一个公告牌。所以非常容易地联想到观察者模式,事实上,作为公告牌的“中介模式”就是观察者模式。
于是,由于中介者Party现在作为交互平台,它被所有感兴趣的参与者观察!再次不同于[GoF]中介者模式。[GoF]中,它写道“将Mediator实现为一个观察者,各Colleague作为Subject”。而此时,我们的代码是:Mediator实现为一个Subject,各Colleague作为Observer。
这一“平台式中介的代码,网上随处可见。
对象和类
[GoF]中介者模式:“定义一个中介对象来封装系列对象之间的交互。中介者使各个对象不需要显示地相互引用,从而使其耦合性松散..."
这也给我们带来一个问题:这里的”对象“是什么东西?
在Java等面向对象的语言中,yqj2065一直强调:类是第一性的。程序员编程针对的都是类而非对象。事实上,[GoF]的中介者模式——“管家式”委派方式中,才体现了一个类(的对象)发生状态改变,将导致许多其他类(的对象)发生相应的变化。和股票交易所、婚姻介绍所等不同,后者虽然参与者有诸多对象,真正的类不多而且交互简单。
有人说了:Party中有的男女对上眼了,会一直聊到Party结束;有的男生可以一直聊篮球;有的女生只和女生聊,有的女生只和名字好听的人聊……你怎么处理这些对象?
还是一句话:我将处理的不是对象而是类——匿名类!你可以用匿名类或λ表达式为客户留下无限的扩展空间。
总之,股票交易所、婚姻介绍所、聊天室的例子,都不是[GoF]中介者模式。