10.2 解 决 方 案

10.2.1 使用中介者模式来解决问题

用来解决上述问题的一个合理的解决方案就是中介者模式(Mediator)。那么什么是中介者模式呢?

1.中介者模式的定义

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

2.应用中介者模式来解决问题的思路

仔细分析上面的问题,根本原因就在于多个对象需要相互交互,从而导致对象之间紧密耦合,不利于对象的修改和维护。

中介者模式的解决思路很简单,跟电脑的例子一样,中介者模式通过引入一个中介对象,让其他的对象都只和中介对象交互,而中介对象知道如何和其他所有的对象交互,这样对象之间的交互关系就没有了,从而实现了对象之间的解耦。

对于中介对象而言,所有相互交互的对象,被视为同事类,中介对象就是来维护各个同事之间的关系,而所有的同事类都只是和中介对象交互。

每个同事对象,当自己发生变化的时候,不需要知道这会引起其他对象有什么变化,它只需要通知中介者就可以了,然后由中介者去与其他对象交互。这样松散耦合带来的好处是,除了让同事对象之间相互没有关联外,还有利于功能的修改和扩展。

有了中介者以后,所有的交互都封装到中介者对象里面,各个对象就不再需要维护这些关系了。扩展关系的时候也只需要扩展或修改中介者对象就可以了。

10.2.2 中介者模式的结构和说明

中介者模式的结构如图10.3所示:

图片

图10.3 中介者模式结构示意图

■ Mediator:中介者接口。在里面定义各个同事之间交互需要的方法,可以是公共的通信方法,比如changed方法,大家都用,也可以是小范围的交互方法。

■ ConcreteMediator:具体中介者实现对象。它需要了解并维护各个同事对象,并负责具体的协调各同事对象的交互关系。

■ Colleague:同事类的定义,通常实现成为抽象类,主要负责约束同事对象的类型,并实现一些具体同事类之间的公共功能,比如,每个具体同事类都应该知道中介者对象,也就是具体同事类都会持有中介者对象,都可以定义到这个类里面。

■ ConcreteColleague:具体的同事类,实现自己的业务,在需要与其他同事通信的时候,就与持有的中介者通信,中介者会负责与其他的同事交互。

10.2.3 中介者模式示例代码

(1)先来看看所有同事的父类的定义。

按照前面的描述,所有需要交互的对象都被视为同事类,这些同事类应该有一个统一的约束。而且所有的同事类都需要和中介者对象交互,换句话说就是所有的同事都应该持有中介者对象。

因此,为了统一约束众多的同事类,并为同事类提供持有中介者对象的公共功能,先来定义一个抽象的同事类,在里面实现持有中介者对象的公共功能。

注意

要提醒一点,下面示例的这个抽象类是没有定义抽象方法的,主要是用来约束所有同事类的类型。

示例代码如下:

284fedbd3d1543cf91cfc4ff3184657d

(2)再来看看具体的同事类,在示意中它们的实现是差不多的。示例代码如下:

8c8636743ea74a32acea9d67c0bf0308

719c3f0bb9464d3dbc4ef9f0bc0907f4

同事类B的实现。示例代码如下:

b89447e0fbf04a9b9a4b4064044eaf11

(3)接下来看看中介者的定义。示例代码如下:

9bbd124c93054d289a6a45499b4da586

(4)最后来看看具体的中介者实现。示例代码如下:

7b0bf9145b9d4982901058bf36544a67

c44d0a1e0bac4507b60f4adc65b60954

8cc220a8771c4be0aab2ac49e1659b22

10.2.4 使用中介者模式来实现示例

要使用中介者模式来实现示例,那就要区分出同事对象和中介者对象。很明显,主板是作为中介者,而光驱、CPU、声卡、显卡等配件,都是作为同事对象。

根据中介者模式的知识,设计出示例的程序结构,如图10.4所示。

图片

图10.4 使用中介者模式实现示例的结构示意图

下面来看看代码实现,会更清楚。

(1)先来看看所有同事的抽象父类的定义,跟标准的实现是差不多的。示例代码如下:

0342827aa8ca49958d0802b40b2b793c

(2)定义众多的同事。

定义好了同事的抽象父类,接下来就应该具体地实现这些同事类了,按照顺序一个一个来。

先看看光驱类吧。示例代码如下:

70868bf6e24c4ca19724957e4b19e35f

46cc9696c08a4fb2b8aae477c5d61d77

接下来该看看CPU类了。示例代码如下:

4fb8f92a2249459796fca4027c7f2c3d

6dfd6c0548bf4db38f0142b1613267a4

下面该来看看显示的同事类了。显卡类的示例代码如下:

a80a70626ce3407a855ab66c4009439e

同样的,看看声卡的处理类,示例代码如下:

3ecebebe1d6b4ae884b9bc75dbe46a0a

(3)定义中介者接口。

由于所有的同事对象都要和中介者交互,下面来定义出中介者的接口,功能不多,提供一个让同事对象在自身改变的时候来通知中介者的方法。示例代码如下:

74a89230014a4f1e87839bdd7af4758f

(4)实现中介者对象。

中介者的功能稍微多一点,它需要处理所有的同事对象之间的交互。好在我们要示例的东西并不麻烦,仅有两个功能处理而已。示例代码如下:

32367c26605b4ec0afa735ebb5484d47

20ad7b5a56c3453e82a55a9c8136f14f

8640fa8aa26e40efa258b77a5f70334d

c4b77bb0118e4b19adb51f876fdd4cf4

(5)看个电影享受一下。

定义完了同事类,也实现处理了它们交互的中介者对象,该来写个客户端使用它们。来看个电影,好好享受一下。写个客户端测试一下,看看效果。示例代码如下:

dead6290fd1e41c2bb312e0888973fb1

03405b5c309043f8b3f98089edb01277

测试结果如下:

您正观看的是:设计模式

画外音:值得好好研究

如同上面的示例,对于光驱对象、CPU对象、显卡对象和声卡对象,需要相互交互,虽然只是简单演示,但是也能看出来,它们的交互是比较麻烦的,于是定义一个中介者对象——主板对象,来维护它们之间的交互关系,从而使得这些对象松散耦合。

如果这个时候需要修改它们的交互关系,直接到中介者里面修改就好了,也就是说它们的关系已经独立封装到中介者对象里面了,可以独立地改变它们之间的交互关系,而不用去修改这些同事对象。