25.世界需要和平 - 中介者模式(Mediator)

25.1 世界需要和平!

  • 时间: 7月5日20点   地点: 小菜大鸟住所的客厅   人物: 大鸟, 小菜

     “大鸟, 今晚学习什么模式?”

     “哦, 今天讲中介者模式.” 大鸟说.
     “中介? 就是那个房产或出国留学的中介.”
     “哈, 是的, 就是那个词, 中介模式又叫调停者模式. 其实就是中介人或者调停者的意思.”
     “举个例子来看看.”
    ”比如伊拉克又接连发生了很多爆炸事件, 战争给人带来的痛苦真的是无法拟补的伤害.” 大鸟感慨道, “世界真的是需要和平的.呀.”
    ”是呀, 如果不是伊拉克战争, 可能就么有这么多事情了. 相比叫我们可能真的太幸福了.”
    ”在比如巴以问题, 伊朗核问题, 朝鲜核问题以及各国间的政治交流问题, 构成了极为复杂的国际形势.”
    ”这些和今天要讲的中介模式有什么关系?”
    ”你想想看, 由于各国间的代表的利益不同, 所以矛盾冲突时难免的, 但如果有这样一个组织, 由各国的代表组成, 用来维护国际和平与安全, 解决国际间的经济, 社会, 文化 和人道主义性质的问题, 不就很好嘛?”
    ”啊, 你指的就是联合国组织吧, 我明白了, 他就是一个调停者, 中介者的角色.”
     “是的, 各国之间关系复杂, 战略盟友, 战略伙伴, 战略对手, 利益先关者等等等, 各国政府都需要投入大量的人力物力在政治, 经济, 外交方面来搞好这些关系, 但不过如何努力, 国与国之间的关系还是会随着时间和社会发展而发生变化. 在第二次世界大战以前, 由于没有这样一个民主中立里的协调组织, 使得就出现了法西斯联盟, 给人历史上造成了最大的灾难—二战. 而自1945年成立了联合国之后, 地球上再也没有发生世界范围的战争, 可以说, 联合国对世界和平的贡献不可估量.”
     “啊, 原来没大型战争的原因在这里. 拿着和我们学的软件设计模式有什么关系呢?”
    ”你想呀, 国与国之间的关系, 就类似于不同的对象与对象之间的关系, 这就要求对象之间需要知道其他所有对象, 尽管讲一个系统分隔成许多对象通常可以增加其复用性, 但是对象间相互连接的激增又会降低其可复用. 你知道为什么会这样?”
     “我想是因为大量的链接使得一个对象不可能在没有其他对象的支持下工作, 系统表现为一个不可分割的整体, 所以, 对系统行为的人和较大的改动就十分困难.



     “总结的很好嘛, 要解决这样的问题, 可以应用什么原则?”
     “我记得之前讲过的叫 ‘迪米特法则’, 如果两个类不必批次直接通信, 那么这两个类就不应当发生直接的相互作用. 如果其中一个雷需要调用另一个类的某一个方法的haunted, 可以通过第三者转发这个调用. 在这里, 你的意思是, 国与国之间完全可以通过 ‘联合国’ 这个中介来发生关系, 而不用直接通信.”
     “是呀, 通过中介者对象, 通过中介者, 可以将系统的网状结构编程以中介者为中心的星型结构, 每个具体的对象不再通过直接的联系与另一个对象发生作用, 而是通过 ‘中介者’ 对象与另一个对象发生相互左右. 中介者对象的设计, 是的系统的结构不会因为新对象的引入而造成大量的修改工作.”



     “当时你对我解释 ‘迪米特法则’ 的时候, 是以我公司的 IT 部门的管理为例子的, 其实让我一个刚进公司的人去请求任何一个不认识的 IT 部门的同事帮忙室友困难的, 但如果有 IT 主管来协调工作, 就不至于发生我第一天上班却没有电脑进行工作的局面. IT 主管就是一个 ‘中介者’ 对象了.”



     “Colleague 叫做抽象同事类, 而 ConcreteColleague 是具体同事类, 每个具体同事只知道自己的行为, 二不了解其他同事的情况, 但他们却都认识中介者对象, Mediator 是抽象中介者, 定义了同时对象到中介者对象的接口, ConcreteMediator 是具体的中介者对象, 实现抽象类的方法, 他需要直达所有具体同事类, 并从具体同事类接收消息, 向具体同事类发出命令.”
     Mediator 抽象中介者类.

1
2
3
4
public abstract class Mediator {
// 定义一个抽象的发送消息的方法, 得到同事对象和发送消息
public abstract void send(String message, Colleague colleague);
}

     Colleague 抽象同事类

1
2
3
4
5
6
7
8
public abstract class Colleague {
protected Mediator mediator;
// 通过构造方法获得中介者对象
public Colleague(Mediator mediator) {
this.mediator = mediator;
}
}

     ConcreteMediator 具体中介者类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class ConcreteMediator extends Mediator {
private Colleague colleague1;
private Colleague colleague2;
public void setColleague1(Colleague colleague1) { this.colleague1 = colleague1; }
public void setColleague2(Colleague colleague2) { this.colleague2 = colleague2; }
@Override
public void send(String message, Colleague colleague) {
if (colleague instanceof ConcreteColleague1) {
colleague2.notifyColleague(message);
} else if (colleague instanceof ConcreteColleague2){
colleague1.notifyColleague(message);
}
}
}

     ConcreteColleague1 和 ConcreteColleague2 等各种同事对象

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
30
public class ConcreteColleague1 extends Colleague{
public ConcreteColleague1(Mediator mediator) {
super(mediator);
}
public void send(String message) {
mediator.send(message, this);
}
public void notifyColleague(String message) {
System.out.println("同事1得到信息: " + message);
}
}
public class ConcreteColleague2 extends Colleague{
public ConcreteColleague2(Mediator mediator) {
super(mediator);
}
@Override
public void notifyColleague(String message) {
System.out.println("同事2得到信息: " + message);
}
public void send(String message) {
mediator.send(message, this);
}
}

     客户端调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static void main(String[] args) {
ConcreteMediator m = new ConcreteMediator();
// 让两个具体类, 认识中介者对象
ConcreteColleague1 c1 = new ConcreteColleague1(m);
ConcreteColleague2 c2 = new ConcreteColleague2(m);
// 让中介者认识各个具体同事类对象
m.setColleague1(c1);
m.setColleague2(c2);
// 具体同事类的发送的信息通过中介者转发
c1.send("你吃饭了吗?");
c2.send("没有呢, 你打算请客?");
}

     “由于有了 Mediator, 使得 ConcreteColleague1 和 ConcreteColleague2 在发送消息和接收消息时, 其实是通过中介者来完成的, 这就减少了它们之间的耦合度了.” 大鸟说, “好了, 该你来练习了, 需求是美国和伊拉克之间的对话都是通过联合国安理会作为中介来完成的.”
     “哦, 这应该没什么大问题, 美国和伊拉克都是国家, 有一个国家抽象类和两个具体国家类就可以了. 但 ‘联合国’ 到底是 Mediator 还是 ConcreteMediator 呢?”
     “这要取决于未来是否有可能扩展中介者对象, 比如你觉得联合国除了安理会, 还有没有可能有其他的机构存在呢?”
     “哈, 大鸟你启发我了, 联合国机构还有如国际劳工组织, 教科文组织, 世界卫生组织, 世界贸易组织等等, 很多的, 所以 Mediator 应该是 ‘联合国机构’, 而 ‘安理会’ 是一个具体的中介者.”
     “很好, 如果不存在扩展的情况, 那么 Mediator 可以与 ConcreteMediator 合二为一.” 大鸟说, “开始吧”

25.3 安理会做中介

     过了近一个小时, 小菜才将代码写了出来.
     代码结构图



     联合国机构类, 相当于 Mediator

1
2
3
4
public abstract class UnitedNations {
// 声明
public abstract void declare(String message, Country colleague);
}

     国家类, 相当于 Colleague 类

1
2
3
4
5
6
7
public abstract class Country {
protected UnitedNations mediator;
public Country(UnitedNations mediator) {
this.mediator = mediator;
}
}

     美国类, 相当于 ConcreteColleague1 类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class USA extends Country {
public USA(UnitedNations mediator) {
super(mediator);
}
public void declare(String message) {
mediator.declare(message, this);
}
public void getMessage(String message) {
System.out.println("美国获得对方的消息: " + message);
}
}

     伊拉克类, 相当于 ConcreteColleague2 类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Iraq extends Country {
public Iraq(UnitedNations mediator) {
super(mediator);
}
public void declare(String message) {
mediator.declare(message, this);
}
public void getMessage(String message) {
System.out.println("伊拉克获得对方的消息: " + message);
}
}

     联合国安理会, 相当于 ConcreteMediator 类

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
public class UnitedNationsSecurityCouncil extends UnitedNations {
private USA colleague1;
private Iraq colleague2;
// 美国
public void setColleague1(USA colleague1) {
this.colleague1 = colleague1;
}
// 伊拉克
public void setColleague2(Iraq colleague2) {
this.colleague2 = colleague2;
}
@Override
public void declare(String message, Country colleague) {
// 声明两个对象之间的通信.
// 如果又是个国家就需要写十个通信
if (colleague instanceof USA) {
colleague2.getMessage(message);
} else if (colleague instanceof Iraq) {
colleague1.getMessage(message);
}
}
}

     客户端调用

1
2
3
4
5
6
7
8
9
10
11
12
public static void main(String[] args) {
UnitedNationsSecurityCouncil UNSC = new UnitedNationsSecurityCouncil();
USA c1 = new USA(UNSC);
Iraq c2 = new Iraq(UNSC);
UNSC.setColleague1(c1);
UNSC.setColleague2(c2);
c1.declare("不准研发核武器, 否则要发动战争!");
c2.declare("我们没有核武器, 也不怕战争.");
}

     结果显示

1
2
伊拉克获得对方的消息: 不准研发核武器, 否则要发动战争!
美国获得对方的消息: 我们没有核武器, 也不怕战争.

     “哈, 小菜呀, 你这样的写法和我写的样例代码有什么差别呀, 除了类名变量名不同, 没什么区别. 这样的代码为何还要写这么长时间呢?”
     “哈, 我边写边思考他是如何做到中介的, 其实最关键的问题在于 ConcreteMediator 这个类必须知道所有的 ConcreteColleague, 这好像有些问题.”
     “你的想法是什么呢?”
     “我觉得尽管这样的设计减少了 ConcreteColleague 类之间的耦合, 但这又是的 ConcreteMediator 责任太多了, 如果它出了问题, 整个系统都会有问题.”

25.4 中介者模式的优缺点

     “说得好, 如果联合国安理会出了问题, 当然会对全世界造成影响. 所以说, 中介者模式很容易在系统中应用, 也很容易在系统中勿用. 当系统中出现了 ‘多对多’ 交互复杂的对象群时, 不要急于使用中介者模式, 而要先反思你的系统在设计上合不合理. 你来总结一下中介者模式的优缺点吧.”
     “我觉得中介模式的有点首先是Mediator 的出现, 减少了个各类的耦合, 是的可以独立的改变和复用个 Mediator 和 Colleague 对象, 比如任何国家的改变不会影响到其他国家, 而只是与安理会发生变化. 其次, 由于把对象如何协作进行了抽象, 将中介作为一个对立的概念并封装在一个对象中, 这样关注的对象就从个对象本身转移掉它们的交互上来, 也就是站在一个更宏观的角度去看待这个系统. 比如巴以冲突, 本来只算是国与国之间的矛盾, 一次各自的看法都比较狭隘, 但站在联合国安理会的角度, 就可以从全球化, 也就是更客观的角度来看待这个问题, 在调停和维和上作出贡献.”
     “哇, 小菜不简单, 一个小时的编码, 原来想到这么多, 不容易不容易, 你说的非常好, 用中介者模式确实可以从更宏观的角度看待问题, 那中介者模式的缺点呢.”
     “应该就是你刚才提到的, 具体中介者类 ConcreteMediator 可能会因为 ConcreteColleague 越来越多, 而变得非常复杂, 不容易维护了.”
     “是的, 由于 ConcreteMediator 控制了集中化, 于是就把交互复杂性变为中介者复杂性, 这就使得中介者 ConcreteMediator 比任何一个 ConcreteColleague 都复杂. 事实上, 联合国安理会秘书长的工作应该是非常的繁忙的, 谁叫他是 ‘全球最大的官’ 呢. 也正是因为如此, 中介者模式的优点来自于集中控制, 缺点也是它, 使用时要考虑清楚哦.”
     “啊, 这么讲, 我感觉中介者模式的应用很少啊.”
     “小菜, 实际上你一直在用他而不知啊.” 大鸟得意的说.
     “哦, 有吗? 什么时候?” 小菜疑惑着.
     “你平时用 .net 写的 windows 应用程序中的 Form 或是 Web 网站程序中的 aspx 就是典型的中介者啊.”
     “啊, 不会吧, 这怎么讲呢?”
     “比如计算其程序, 上面有菜单控制, 文本控件, 多个按钮和一个 Form 窗体, 每个控件之间的通信都是通过谁来完成的? 他们是否知道彼此之间的存在?”



     “哦, 我知道了, 每个控件的代码都被封装了, 所以他们的实例是不会知道其他空间对象的存在的, 比如点击数字按钮要在文本控件中显示数字, 按照我以前的想法应该在 button 类中编写给 textbox 类实例赋值的方法, 造成这两个类的耦合, 这其实是非常不合理的. 但实际情况是他们都有事件机制, 而事件的执行都是在 Form 窗体中完成, 也就是说所有控件的交互都是由Form 窗体作为中介, 操作各个对象, 这确实是中介者模式的应用.”
     “是的说的很好.” 大鸟鼓励到. “中介者模式一般应用于一组以定义良好但是复杂的方式进行通信的场合, 比如刚才提到的窗体Form 对象和 Web 的aspx 页面, 以及向定制一个分布在多个类中的行为, 而又不想生成太多类的场合.
     “明白, 回到刚才联合国的例子……”

~感谢捧场,您的支持将鼓励我继续创作~