中介者模式
不同类之间的通信大部分是通过直接引用或者指针引用的方式,但是在某些情况下,我们不希望对象之间知道彼此的存在。比如飞机场的降落管控事件,我们不希望不同的飞机间通过相互商议的形式决定谁先降落、谁后降落,而是希望他们听从地面管控中心的统一管理。这就是中介者模式机制所面向的问题。即一个对象管理多个对象协同工作>的关系。
网络聊天室
网络聊天室是一个经典案例。
参与者
聊天室中的参与者可以简单描述为:
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; 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;
class Person;
class ChatRoom{ public: vector<Person*> people; void join(Person* p); 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中各个控件之间的中介者模式编码为例。