0%

中介者模式

中介者模式

不同类之间的通信大部分是通过直接引用或者指针引用的方式,但是在某些情况下,我们不希望对象之间知道彼此的存在。比如飞机场的降落管控事件,我们不希望不同的飞机间通过相互商议的形式决定谁先降落、谁后降落,而是希望他们听从地面管控中心的统一管理。这就是中介者模式机制所面向的问题。即一个对象管理多个对象协同工作>的关系。

网络聊天室

网络聊天室是一个经典案例。

参与者

聊天室中的参与者可以简单描述为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Person{
Person(const string& name);
string name;
ChatRoom* room = nullptr;
vector<string>chat_log;
void receive(const string& origin, const string& message){
string s{ origin + ": \"" + message + "\"" };
cout << "[" << name << "'s chat session] " << s << endl;
chat_log.emplace_back(s);
};
void say(const string& message) const{
room->broadcast(name, message);
};
void pm(const string& who, const string& message) const{
room->message(name, who, message);
};
};

这个类包含一个构造函数、名字、指向聊天室的指针、聊天记录、三个成员函数。

receive()允许我们接收消息,通常还会在屏幕上显示以及将内容保存到消息日志中。

say()允许此人向房间中每个人发送消息。

pm()是私人消息传递功能,您需要指定消息所针对的人的姓名。

say()和pm()会将操作转到聊天室。

聊天室

那么让我们来实现一个简单的聊天室。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class ChatRoom{
vector<Person*> people; // assume append-only
void join(Person* p){
string join_msg = p->name + " joins the chat";
broadcast("room", join_msg);
p->room = this;
people.push_back(p);
};
void broadcast(const string& origin, const
string& message){
for (auto p : people)
if (p->name != origin)
p->receive(origin, message);
};
void message(const string& origin, const string& who,const string& message){
auto target = find_if(begin(people), end(people),[&](const Person* p) { return p->name == who; });
if (target != end(people)){
(*target)->receive(origin, message);
}
};
};

ChatRoom API非常简单,join()让一个人加入房间(暂时只进不出,后面讨论线程安全问题时再考虑)broadcast()将消息发送给其他所有人、message()发送私人消息。

源代码实现

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#include <iostream>
#include<vector>
#include<string>

using namespace std;

//前置声明,因为ChatRoom需要使用Person类的成员
class Person;

//聊天室类,保存有当前聊天室内人员信息,允许某个成员发送全体消息,允许人员加入,允许人员发送私信
class ChatRoom{
public:
vector<Person*> people;
void join(Person* p);
//接口函数声明,类中声明与实现分离,在编译器编译时不会报错,因为实现的代码在Person类完整声明之后
void broadcast(const string& origin, const
string& message);
void message(const string& origin, const string&who,const string& message);
};

//参与者类,可以发送消息、接受消息、保存聊天记录,拥有个人标识以及所在房间标识
class Person{
public:
Person(const string& name):name(name){};
string name;
ChatRoom*room = nullptr;
vector<string>chat_log;
void receive(const string& origin,const string& message){
string s{origin + ": \"" + message + "\""};
cout<< "[" << name << "'s chat session] " << s << endl;
chat_log.emplace_back(s);
};
void say(const string& message){
room->broadcast(name, message);
};
void pm(const string& who, const string& message){
room->message(name, who, message);
};
};
//类方法实现部分
void ChatRoom::join(Person *p){
string join_msg = p->name + " joins the chat";
broadcast("room", join_msg);
p->room = this;
people.push_back(p);
};
void ChatRoom::broadcast(const string &origin, const string &message){
for (auto p : people)
if (p->name != origin)
p->receive(origin, message);
};
void ChatRoom::message(const string &origin, const string &who, const string &message){
auto target = find_if(begin(people), end(people),[&](const Person* p) { return p->name == who; });
if (target != end(people)){
(*target)->receive(origin, message);
}
};
//主函数部分
int main()
{
ChatRoom room;
Person john{ "john" };
Person jane{ "jane" };
room.join(&john);
room.join(&jane);
john.say("hi room");
jane.say("oh, hey john");
Person simon("simon");
room.join(&simon);
simon.say("hi everyone!");
jane.pm("simon", "glad you could join us, simon");
return 0;
}

在聊天室的示例中,每当有人发布消息时,参与者都需要通知。即是说:中介者拥有一个所有参与者共享的事件,参与者可以订阅该事件以接收通知,他们也可以触发该事件,从而触发通知。

优点

中介者模式建议停止组件之间的相互联系而使它们相互独立。这些组件之间的交流必须调用特殊的中介者对象, 通过中介者对象重定向调用行为, 以间接的方式进行合作。 最终, 组件仅依赖于一个中介者类, 无需与多个其他组件相耦合。能让你减少对象之间混乱无序的依赖关系。

如资料编辑表单, 对话框 (Dialog) 类本身将作为中介者。

示意图
绝大部分重要的修改都在实际表单元素中进行。 如提交按钮,当用户点击按钮后, 它必须对所有表单元素数值进行校验。如果使用中介者模式,它只需要发送消息给中介者即可,中介者会自行校验数值并将任务委派下去,这样一来,按钮不再与其他表单元素相关联。 在 MVC 模式中, 控制器是中介者的同义词。在 C++ 代码中中介者模式最常用于帮助程序 GUI 组件之间的通信。

下面以真实编码示例中的UI中各个控件之间的中介者模式编码为例。

1
google