1 实例
在公司里,有不同的职位,比如销售人员,开发人员,系统维护人员,主管等等,经常不同的人会发出不同广播消息,比如主管通知大家会议室在何时被占用,系统维护人员通知大家系统将升级 win7,因为 xp 将不再得到维护。并且公司内有不同类型的广播消息,比如公司销售会议只应该通知主管级别的人物。
有不同类型的广播消息,不同职位接收和发送不同类型的消息给相应职位的人,整个公司的交互相对复杂,如何做到以下呢?
- 理清和维护好现有的人员交互;
- 灵活的扩展不同类型广播和不同消息群组。
2 中介者模式(Mediator)
2.1 目的
定义一个对象来封装一组对象如何交互。中介者模式使各个对象不显示的互相引用,并让你独立的改变它们的交互,从而达到提高松耦合的效果。
2.2 实现
引入中介者模式集中式控制。由中介者模式来减少交互的复杂度,由它集中理清交互的关系。代码实例在这里。
2.2.1 中介者类
中介者类实现同事间的消息广播:
- 模板类提供不同类型消息的复用;
- 提供同事的注册和脱离不同消息的广播群组:
RegisterColleague
和UnregisterColleague
; - 发送消息广播事件:
FireEvent
; - 用
vector
维护一个同事消息队列来对注册入队列的同事发生广播事件。
template<typename EventArgType> class Mediator { public: typedef vector<ColleagueEvent<EventArgType>* > EventList; virtual ~Mediator() {} private: EventList colleagues; static Mediator<EventArgType> *instance; static Mediator<EventArgType>& GetInstance() { if (!instance) { instance = new Mediator(); } return *instance; } void RegisterColleague(ColleagueEvent<EventArgType> *col_event) { colleagues.push_back(col_event); } void FireEvent(ColleagueEvent<EventArgType> *source, EventArgType event_arg) { for (unsigned int i = 0; i < colleagues.size(); ++i) { if (colleagues[i] != source) { colleagues[i]->handlerProc(source->event_context, event_arg, colleagues[i]->event_context); } } } void UnregisterColleague(ColleagueEvent<EventArgType> *col_event) { typename EventList::iterator it = find(colleagues.begin(), colleagues.end(), col_event); if (it != colleagues.end()) { colleagues.erase(it); } } friend class ColleagueEvent<EventArgType>; };
2.2.2 同事消息事件类
同事消息事件类封装消息事件:
- 模板类提供不同类型消息的复用;
- 消息的注册同事;
- 消息的处理函数,不同同事实现各自的处理函数,当由消息到来时,中介者类调用各个同事的消息处理函数来操作;
- 用中介者类注册和脱离消息群组;
- 用中介着类出发消息事件;
template<typename EventArgType> class ColleagueEvent { public: typedef void (*ColleagueEventHandler) (Colleague *source, EventArgType event_arg, Colleague *context); ColleagueEvent(Colleague *source, ColleagueEventHandler event_proc) : event_context(source), handlerProc(event_proc) { Mediator<EventArgType>::GetInstance().RegisterColleague(this); } virtual ~ColleagueEvent() { Mediator<EventArgType>::GetInstance().UnregisterColleague(this); } void FireEvent(EventArgType event_arg) { Mediator<EventArgType>::GetInstance().FireEvent(this, event_arg); } private: Colleague *event_context; ColleagueEventHandler handlerProc; friend class Mediator<EventArgType>; };
2.2.3 公司人员抽象类
为同事定义接口。暂时是空的。
class Colleague { protected: Colleague() {} virtual ~Colleague() {} };
2.2.4 普通员工消息类型类
消息有很多不同类型,如普通员工消息类型,主管级消息等等,这里以普通员工消息为例。
class StaffMsg { public: StaffMsg(string name, string data) : msg_name_(name), msg_data_(data) { } virtual ~StaffMsg() {} string msg_name() {return msg_name_;} string msg_data() {return msg_data_;} private: string msg_name_; string msg_data_; };
2.2.5 雇员基类
有雇主和雇员之分,这里以雇员为例。
class Employee : public Colleague { public: Employee(string title, string name) : title_(title), name_(name) { } virtual ~Employee() {} string title() {return title_;} string name() {return name_;} protected: string title_; string name_; };
2.2.6 普通员工类
普通员工基于雇员类。维护普通员工消息事件实例,负责把自己注册到相应的中介者消息队列,实现普通员工如何处理接收到的消息。
class GeneralStaff : public Employee { public: GeneralStaff(string title, string name); virtual ~GeneralStaff(); static void OnColleagueEvent(Colleague *source, StaffMsg data, Colleague* context); protected: ColleagueEvent<StaffMsg> general_staff_event_; }; GeneralStaff::GeneralStaff(string title, string name) : Employee(title, name), general_staff_event_(this, OnColleagueEvent) { } void GeneralStaff::OnColleagueEvent(Colleague *source, StaffMsg data, Colleague* context) { Employee *src_colleague = static_cast<Employee *> (source); Employee *ctx_colleague = static_cast<Employee *> (context); cout << endl << ctx_colleague->title() << " - " << ctx_colleague->name() << " is notified by " << src_colleague->title() << " - " << src_colleague->name() << " of STAFF Event " << data.msg_name() << " with " << data.msg_data(); }
2.2.7 销售人员类
class SalesMen : public GeneralStaff { public: explicit SalesMen(string name) : GeneralStaff("Sales Man", name) { } virtual ~SalesMen() {} };
2.2.8 系统维护人员类
系统维护人员类注册普通员工消息队列,并有发起软件升级通知的权利。
class SysAdmin : public GeneralStaff { public: explicit SysAdmin(string name); virtual ~SysAdmin(); void AdviceForSoftwareUpdate(string sw_name); }; SysAdmin::SysAdmin(string name) : GeneralStaff("Sys Admin", name) { } void SysAdmin::AdviceForSoftwareUpdate(string sw_name) { general_staff_event_.FireEvent(StaffMsg("Software Update Advice", sw_name)); }
2.2.9 主管人员类
系统维护人员类注册普通员工消息队列,并有发起预订会议室通知的权利。
class Manager : public GeneralStaff { public: explicit Manager(string name); virtual ~Manager(); void BookMeetingRoom(string meeting_room_name); }; Manager::Manager(string name) : GeneralStaff("Manager", name) { } void Manager::BookMeetingRoom(string meeting_room_name) { general_staff_event_.FireEvent(StaffMsg("Meeting Room Booking", meeting_room_name)); }
2.2.10 总的测试
- 建立各个员工;
- 发起不同类型消息测试。
int main(int argc, char *argv[]) { Manager mng1("Vivek"), mng2("Pradeep"); SysAdmin sys_admin("Sony"); SalesMen sl1("Dave"), s12("Mike"), s13("Allen"); mng1.BookMeetingRoom("Big Room"); cout << endl; sys_admin.AdviceForSoftwareUpdate("Win7"); cout << endl; return 0; }
结果:
Manager - Pradeep is notified by Manager - Vivek of STAFF Event Meeting Room Booking with Big Room Sys Admin - Sony is notified by Manager - Vivek of STAFF Event Meeting Room Booking with Big Room Sales Man - Dave is notified by Manager - Vivek of STAFF Event Meeting Room Booking with Big Room Sales Man - Mike is notified by Manager - Vivek of STAFF Event Meeting Room Booking with Big Room Sales Man - Allen is notified by Manager - Vivek of STAFF Event Meeting Room Booking with Big Room Manager - Vivek is notified by Sys Admin - Sony of STAFF Event Software Update Advice with Win7 Manager - Pradeep is notified by Sys Admin - Sony of STAFF Event Software Update Advice with Win7 Sales Man - Dave is notified by Sys Admin - Sony of STAFF Event Software Update Advice with Win7 Sales Man - Mike is notified by Sys Admin - Sony of STAFF Event Software Update Advice with Win7 Sales Man - Allen is notified by Sys Admin - Sony of STAFF Event Software Update Advice with Win7
3 总结
3.1 中介者模式(Mediator)结构
3.2 组成
- 抽象中介者(Mediator)为与同事(Colleague)对象交流定义一个接口。
- 具体中介者(ConcreteMediator)
- 实现与同事(Colleague)对象协作的合作行为。
- 知道和维护它的同事类。
- 同事类(Colleague classes)
- 每个同事类知道它的中介者对象。
- 每个同事类任何它应该时与它的中介者交流,否则就与其他同事类交流。
3.3 应用场景
- 一组对象交流在很好定义的却复杂的方式上。导致的互相依赖性并没有好的组织而且难理解时。
- 复用一个对象是困难的,因为它引用并交流与许多其他对象。
- 分布在很多类中的一个行为需要被定制但不能进行太多子类化。
3.4 缺点
中介者模式集中式控制。中介者模式以减少交互的复杂度而增加中介者的复杂度。因为一个中介者类封装了协议,它会变得比任何同事类都复杂。这会导致中介者类自身是个难维护的庞然大物。