中介者模式(行为型)

前言


在互联网通信高度发达的今天,大家在过节的时候都是通过QQ或微信来发祝福短信,有时候,由于朋友太多,一个一个的发送消息比较麻烦。幸亏QQ和微信有群发的功能,你只需要选择发送的对象,编辑发送一次信息,其他的所有群里的朋友都可以收到祝福了。通过引入群的机制,将极大减少系统中用户之间的两两通信,用户与用户之间的联系可以通过群来实现。这样的类似“群”一样的中间类来协调这些类/对象之间的复杂关系,以降低系统的耦合度的模式就是本篇将要介绍的中介者模式。

中介者模式


面向对象设计鼓励将行为分布到各个对象中。这种分布可能会导致对象间有许多连接。在最坏的情况下,每一个对象都知道其他所有对象。可以通过将集体行为封装在一个单独的中介者对象中以避免这个问题。中介者负责控制和协调一组对象间的交互。中介者充当一个中介以使组中的对象不再相互显式引用。这些对象仅知道中介者, 从而减少了相互连接的数目,符合迪米特法则(最少知道原则)

中介者模式将一个网状的系统结构变成一个以中介者对象为中心的星形结构,在这个星型结构中,使用中介者对象与其他对象的一对多关系来取代原有对象之间的多对多关系

意图

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

参与者

  • Mediator
    抽象中介者,定义了一个接口,该接口用于与个同事对象之间进行通信

  • ConcreteMediator
    具体中介者,是抽象中介者的子类,通过协调各个同事对象来实现协作行为
    维持了对个同事对象的引用

  • Colleague
    抽象同事类,定义了各个同事类的共有方法,并声明了一些抽象方法来供子类实现
    维持了一个对抽象中介者的引用,其子类可以通过该引用与中介者通信

  • ConcreteColleague
    具体同事类,是抽象同事类的子类,实现了在抽象同事类中声明的抽象方法
    每个同事对象在需要与其他同事对象通信时候,首先通过中介者通信,再通过中介者间接完成与其他同事类的通信

中介者模式的核心在于中介者类的引入,在中介者模式中,中介者类承担了两方面的职责:

  1. 中转作用(结构性):通过中介者提供的中转作用,各个同事对象就不再需要显式引用其他同事,当需要和其他同事进行通信时,可通过中介者来实现间接调用
  2. 协调作用(行为性):中介者可以更进一步的对同事之间的关系进行封装,同事可以一致的和中介者进行交互,而不需要指明中介者需要具体怎么做,中介者根据封装在自身内部的协调逻辑,对同事的请求进行进一步处理,将同事成员之间的关系行为进行分离和封装

模式结构

mediator_common

代码实现

1.首先定义抽象的中介这类Mediator,并提供Notify()接口用于各个同事之间进行通信:

1
2
3
4
5
6
7
8
9
10
// Abstract Mediator 
class Colleague;
class Mediator
{
protected:
list<Colleague*> m_listColleague;
public:
virtual void AttachColleague(Colleague* pColleague) = 0;
virtual void Notify(Colleague *pColleague, string msg) = 0;
};

2.再定义具体中介者类ConcreteMediator,并实现相应的接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Concrete Mediator
class ConcreteMediator : public Mediator
{
public:
virtual void AttachColleague(Colleague* pColleague)
{
m_listColleague.push_back(pColleague); // 加入需要通信的同事对象
}
// 指定的Colleague pColleague发送消息(通过Mediator),其他的Colleague接受消息
virtual void Notify(Colleague *pColleague, string msg)
{
for (list<Colleague*>::iterator it = m_listColleague.begin();
it != m_listColleague.end();
++it)
{
if ((*it) != pColleague)
{
(*it)->RecvMsg(msg); // 调用其他同事类的RecvMsg方法
}
}
}
};

3.再定义抽象的同事类Colleague,该同事类具有抽象中介者对象的引用,并声明了各个同事子类需要实现的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
// Abstract Colleague
class Colleague
{
protected:
Mediator* m_pMediator;
string m_Name;
public:
Colleague(Mediator* pMediator, string name): m_pMediator(pMediator),m_Name(name) {};
public:
virtual void SendMsg(string msg) = 0;
virtual void RecvMsg(string msg) = 0;
virtual string GetName() = 0;
};

4.定义两个具体的同事类ConcreteColleagueAConcreteColleagueB,实现抽象同事类的相应接口:

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// Concrete Colleague A
class ConcreteColleagueA : public Colleague
{
public:
ConcreteColleagueA(Mediator* pMediator, string name): Colleague(pMediator, name) {};
public:
virtual void SendMsg(string msg)
{
cout << m_Name << "[CCA] Send Msg: "<< msg << endl;
m_pMediator->Notify(this, msg);
//(依赖方法)调用中介者的方法,与其他具体同事通信
}
virtual void RecvMsg(string msg)
{
cout << m_Name << "[CCA] Recv Msg: "<< msg << endl;
//(自身方法) 处理自己的行为
}
public:
virtual string GetName()
{
return m_Name;
}
};

// Concrete Colleague B
class ConcreteColleagueB : public Colleague
{
public:
ConcreteColleagueB(Mediator* pMediator, string name): Colleague(pMediator, name) {};
public:
virtual void SendMsg(string msg)
{
cout << m_Name << "[CCB] Send Msg: "<< msg << endl;
m_pMediator->Notify(this, msg);
//(依赖方法)调用中介者的方法,与其他具体同事通信
}
virtual void RecvMsg(string msg)
{
cout << m_Name << "[CCB] Recv Msg: "<< msg << endl;
//(自身方法) 处理自己的行为
}
public:
virtual string GetName()
{
return m_Name;
}
};

5.测试中介者模式:

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
void MediatorTest_General()
{
Mediator *pM = new ConcreteMediator();

Colleague *pCA = new ConcreteColleagueA(pM, "Monitor1");
Colleague *pCA2 = new ConcreteColleagueA(pM, "Monitor2");
Colleague *pCB = new ConcreteColleagueB(pM, "Student1");
Colleague *pCB2 = new ConcreteColleagueB(pM, "Student2");

pM->AttachColleague(pCA);
pM->AttachColleague(pCA2);
pM->AttachColleague(pCB);
pM->AttachColleague(pCB2);

// Monitor 1 Send Msg
pCA->SendMsg("Hello Everyone I am Monitor 1 ");

// Monitor 2 Send Msg
pCA2->SendMsg("Hello Everyone I am Monitor 2 ");

// Student 2 Send Msg
pCB->SendMsg("Hello Everyone I am Student 1");

SAFE_RELASE_POINTER(pM);
SAFE_RELASE_POINTER(pCA);
SAFE_RELASE_POINTER(pCA2);
SAFE_RELASE_POINTER(pCB);
SAFE_RELASE_POINTER(pCB2);
};

6.运行结果:

Monitor1[CCA] Send Msg: Hello Everyone I am Monitor 1
Monitor2[CCA] Recv Msg: Hello Everyone I am Monitor 1
Student1[CCB] Recv Msg: Hello Everyone I am Monitor 1
Student2[CCB] Recv Msg: Hello Everyone I am Monitor 1
Monitor2[CCA] Send Msg: Hello Everyone I am Monitor 2
Monitor1[CCA] Recv Msg: Hello Everyone I am Monitor 2
Student1[CCB] Recv Msg: Hello Everyone I am Monitor 2
Student2[CCB] Recv Msg: Hello Everyone I am Monitor 2
Student1[CCB] Send Msg: Hello Everyone I am Student 1
Monitor1[CCA] Recv Msg: Hello Everyone I am Student 1
Monitor2[CCA] Recv Msg: Hello Everyone I am Student 1
Student2[CCB] Recv Msg: Hello Everyone I am Student 1
  1. 如果需要引入新的具体同事类,只需要继承抽象同事类并实现其中的方法即可,由于具体同事类之间并无直接的引用关系,因此原有所有同事类无须进行任何修改,它们与新增同事对象之间的交互可以通过修改或者增加具体中介者类来实现
  2. 如果需要在原有系统中增加新的具体中介者类,只需要继承抽象中介者类(或已有的具体中介者类)并覆盖其中定义的方法即可,在新的具体中介者中可以通过不同的方式来处理对象之间的交互,也可以增加对新增同事的引用和调用

使用场景

  • 系统中对象之间存在复杂的引用关系,系统结构混乱且难以理解
  • 一个对象由于引用了其他很多对象并且直接和这些对象通信,导致难以复用该对象
  • 想通过一个中间类来封装多个类中的行为,而又不想生成太多的子类。可以通过引入中介者类来实现,在中介者中定义对象交互的公共行为,如果需要改变行为则可以增加新的具体中介者类

优缺点

  • 优点
    • 减少了子类生成,Mediator将原本分布于多个对象间的行为集中在一起。改变这些行为只需生成Meditator的子类即可。这样各个Colleague类可被重用
    • 它将各Colleague解耦,Mediator有利于各Colleague间的松耦合。你可以独立的改变和复用各Colleague类和Mediator类
    • 简化了对象协议,用Mediator和Colleague间的一对多的交互来代替多对多的交互。一对多的关系更容易理解、维护和扩展
    • 对对象如何协作进行了抽象,将中介作为一个独立的概念并将其封装在一个对象中,使你将注意力从对象各自本身的行为转移到它们之间的交互上来。这有助于弄清楚一个系统中的对象是如何交互的
  • 缺点
    • 使控制集中化,中介者模式将交互的复杂性变为中介者的复杂性。因为中介者封装了协议,它可能变得比任一个Colleague都复杂这可能使得中介者自身成为一个难于维护的庞然大物

与外观模式和代理模式及适配器模式及观察者模式的区别

1.各自定义:

  • 中介者模式(行为型):用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互
  • 外观模式(结构型):为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用
  • 代理模式(结构型):为其他对象提供一个代理以控制对这个对象的访问
  • 适配器模式(结构型):将一个类的接口转换成客户希望的另外一个接口。adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作
  • 观察者模式(行为型): 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新

2.区别和联系:

  • 外观模式与中介者的不同之处在于外观模式是对一个对象子系统进行抽象,从而提供了一个更为方便的接口。它的协议是单向的,即外观对象对这个子系统类提出请求,但反之则不行。相反,中介者提供了各同事对象不支持或不能支持的协作行为,而且协议是多向的
  • 外观模式是对已有的子系统的所有对象的封装,对子系统提供统一的接口是根本目的。
  • 代理模式是一对一,一个代理只能代表一个对象,对已有对象的封装,控制访问是根本目的。中介者模式则是多对多,中介者的功能多样,客户也可以多个
  • 适配器模式也是一对一的,对已有对象的封装,适配是根本目的
  • 观察者模式涉及到两个概念:观察者和目标,解决的是观察者和众多目标之间通信的问题,不是目标之间的通信的问题,应用场景如手机应用给客户推送消息,注意手机应用是目标,顾客是观察者,而不是反之;而中介者模式也设计两个概念:中介和客户,它解决的是客户之间消息传递问题,应用场景如群组和中介所

中介者模式具体实例


即时通信群发消息问题

使用中介者模式简单实现前言所述的群发消息功能

代码实现
1.定义抽象中介者类IMGroup,声明同事之间通信的notify()接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class IMColleague;
// instance message abstract mediator class: IMGroup
class IMGroup
{
protected:
list<IMColleague*> _listColleague; // 子类使用
public:
virtual void attachColleague(IMColleague* colleague)
// 关联同事对象,子类不需要重写
{
_listColleague.push_back(colleague);
}
virtual void notify(IMColleague* sender, string msg) = 0;
};

2.分别定义两个具体的中介者类WeiXinIMGroupQQIMGroup,并实现同事之间通信的notify()接口:

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
31
32
33
34
35
36
37
38
// instance message weixin mediator class: WeiXinIMGroup
class WeiXinIMGroup : public IMGroup
{
public:
virtual void notify(IMColleague* sender, string msg)
{
for (list<IMColleague*>::iterator it = _listColleague.begin();
it != _listColleague.end(); ++it)
{
if (sender != *it) // 发送者不需要接收消息
{
cout << "[Wei Xin Notify:] ";
(*it)->recvMsg(msg);
//调用其他同事类的接收消息方法,通过中介者(群)来通知其他同事接收消息
}
}
}
};

// instance message qq mediator class: QQIMGroup
class QQIMGroup : public IMGroup
{
public:
virtual void notify(IMColleague* sender, string msg)
{
for (list<IMColleague*>::iterator it = _listColleague.begin();
it != _listColleague.end(); ++it)
{
if (sender != *it) // 发送者不需要接收消息
{
cout << "[QQ Notify:] ";
(*it)->recvMsg(msg);
//调用其他同事类的接收消息方法,通过中介者(群)来通知其他同事接收消息
}
}
}
};

3.定义抽象的同事类IMColleague,声明发送接口sendMsg()和接受消息接口recvMsg():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// instance message abstract colleage class: IMColleague
class IMColleague
{
protected:
IMGroup *_groupMediator; // 子类使用
public:
IMColleague(IMGroup *mediator) : _groupMediator(mediator) {};
//构造注入中介者对象
public:
virtual void setIMGroup(IMGroup *mediator)
//客户端手动注入中介者对象,子类不需要重写
{
_groupMediator = mediator;
}
public:
virtual void sendMsg(string msg) = 0; //dependon method
virtual void recvMsg(string msg) = 0; // self method
};

4.分别定义具体的朋友类FriendIMColleage及亲戚类RelativeIMColleage,实现发送接口sendMsg()和接受消息接口recvMsg()

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
31
32
33
// instance message friend colleage class: FriendIMColleage
class FriendIMColleage : public IMColleague
{
public:
FriendIMColleage(IMGroup *mediator) : IMColleague(mediator) {};
public:
void sendMsg(string msg)
{
cout << "Friend IM Colleage send msg: " << msg << endl;
_groupMediator->notify(this, msg); // 调用中介者对象通知其他同事
}
void recvMsg(string msg)
{
cout << "Friend IM Colleage recv msg: " << msg << endl;
}
};

// instance message friend colleage class: RelativeIMColleage
class RelativeIMColleage : public IMColleague
{
public:
RelativeIMColleage(IMGroup *mediator) : IMColleague(mediator) {};
public:
void sendMsg(string msg)
{
cout << "Relative IM Colleage send msg: " << msg << endl;
_groupMediator->notify(this, msg); // 调用中介者对象通知其他同事
}
void recvMsg(string msg)
{
cout << "Relative IM Colleage recv msg: " << msg << endl;
}
};

5.测试中介者器模式:

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
void MediatorTest_IMCommunication()
{
IMGroup *weixin_mediator = new WeiXinIMGroup();

// 初始化设置im_friend_a,im_friend_b,im_relative同事类的中介者为weixin
IMColleague *im_friend_a = new FriendIMColleage(weixin_mediator);
IMColleague *im_friend_b = new FriendIMColleage(weixin_mediator);
IMColleague *im_relative = new RelativeIMColleage(weixin_mediator);

// 将im_friend_a,im_friend_b,im_relative加入到weixin_mediator的list中:
weixin_mediator->attachColleague(im_friend_a);
weixin_mediator->attachColleague(im_friend_b);
weixin_mediator->attachColleague(im_relative);

im_relative->sendMsg("happy new year!!!"); // im_friend_a、im_friend_b均能收到

// 重新设置im_friend_a,im_relative同事类的中介者为qq,而im_friend_b中介者仍为weixin
IMGroup *qq_mediator = new QQIMGroup();
im_friend_a->setIMGroup(qq_mediator);
im_relative->setIMGroup(qq_mediator);

// 将im_friend_a,im_relative加入到qq_mediator的list中:
qq_mediator->attachColleague(im_friend_a);
qq_mediator->attachColleague(im_relative);
im_relative->sendMsg("happy birthday!!!"); // 只有im_friend_a能收到
};

6.运行结果:

Relative IM Colleage send msg: happy new year!!!
[Wei Xin Notify:] Friend IM Colleage recv msg: happy new year!!!
[Wei Xin Notify:] Friend IM Colleage recv msg: happy new year!!!
Relative IM Colleage send msg: happy birthday!!!
[QQ Notify:] Friend IM Colleage recv msg: happy birthday!!!