C++设计模式新解四 观察者模式+事件订阅

来源:互联网 发布:国际数据公司 收购 编辑:程序博客网 时间:2024/05/21 06:33

定义:定义对象间的一种一对多关系的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

观察者模式又被称为发布/订阅模式,在这种模式中,一个目标物件(被观察者)管理所有相依于它的相关物件(观察者),并且在目标物件的状态发生改变时主动发出通知。这通常通过各物件所提供的方法来实现,观察者模式通常被用来做事件处理系统。

举个例子:老板就是被观察者,员工就是观察者,大家都看着老板的指挥行动,让你往东你就往东,让你往西你就往西,员工在老板处留下了不同的联系方式有Email,电话,手机,QQ,微信等,一旦老板做了决定就会主动通过不同的方式通知到大家。

再举个例子:用户界面是观察者,业务数据可以作为被观察者,用户界面观察业务数据的变化,发现数据变化后,就显示在用户界面上。


至少需要两个角色:观察者类和被观察者类。

在实现观察者模式的多种形式中,比较直观的是一种“注册----通知----撤销注册”的形式。

注册:在被观察者对象中,放置一个容器保存相关的观察者对象。

通知:一旦被观察对象中的状态发生变化,则通知容器中所有的观察者对象做出相应变化。

撤销注册:将被观察者对象容器中某个要撤销的观察者对象删除。


实际上这种实现方式不仅要包含被观察者和观察者类。因为观察者对象要把自己注册到被观察者对象容器中,被观察者不应该过问观察者的具体类型,而应该使用观察者提供的接口。优点是,假定还有别的观察者,那么只要这个观察者也是相同的接口实现即可。基于接口而不是具体实现,为程序提供了强大的灵活性。也就是说要包含所有观察者的基类。

以下为ObserveDesign.h的源代码

#include <map>#include <list>#include <windows.h>#include <iostream>#include <string>#include "stdafx.h"using namespace std;typedef enum {Event_Invalid = 0,Event_Login = 1,Event_Logout = 2,}EVENT_ID;//观察的事件struct MyEvent{EVENT_ID mEventId;UINT mUniqueIndex;MyEvent(EVENT_ID eventId, UINT uniqueIndex):mEventId(eventId),mUniqueIndex(uniqueIndex){}bool operator == (const MyEvent & rhs){return mEventId == rhs.mEventId;}bool operator < (const MyEvent & rhs) const;};struct LoginEvent: public MyEvent{public:LoginEvent(EVENT_ID eventId, UINT uniqueIndex):MyEvent(eventId, uniqueIndex){}};struct LogoutEvent: public MyEvent{public:LogoutEvent(EVENT_ID eventId, UINT uniqueIndex):MyEvent(eventId, uniqueIndex){}};struct MyEntity;struct EventEngine{public:void Attach(MyEvent pEvent, MyEntity * pEntity);void Detach(MyEvent pEvent);void DetachAll();void fire(MyEvent pEvent);private:typedef std::list<MyEntity*> EventList; //单个时间的订阅者列表typedef std::map<EVENT_ID, EventList> EventListMap; //多个事件的订阅者列表,结构体作为Keytypedef EventList::iterator  EventListIter;typedef EventListMap::iterator EventListMapIter;EventListMap m_map;static EventEngine * mInstance;EventEngine(){}public:static EventEngine * getInstance(){if(mInstance == NULL){mInstance = new EventEngine();}return mInstance;}};void fireEvent(MyEvent pEvent){EventEngine::getInstance()->fire(pEvent);}EventEngine * EventEngine::mInstance = NULL;//观察者(建立该类的虚函数是为了让继承自它的类具体实现该虚函数,以便于在事件管理器的代码编写,事件触发时,直接调用CallBack()函数)struct MyEntity{public:MyEntity(int pId):mId(pId){}virtual void CallBack() = 0;void subscribe(MyEvent pEvent){EventEngine::getInstance()->Attach(pEvent, this);}void unSubscribe(MyEvent pEvent){EventEngine::getInstance()->Detach(pEvent);}int getId(){return mId;}private:int mId;};//英雄struct Hero: public MyEntity{public:Hero(int pId):MyEntity(pId){}void CallBack(){cout << "Hero " << getId() << " CallBack" << endl;};};//怪物struct Monster: public MyEntity{public:Monster(int pId):MyEntity(pId){}void CallBack(){cout << "Monster " << getId() << " CallBack" << endl;};};

以下为ObserveDesign.cpp的源代码

#include "ObserveDesign.h"bool  MyEvent::operator < (const MyEvent & rhs) const{return mEventId < rhs.mEventId;}void EventEngine::Attach(MyEvent pEvent, MyEntity * pEntity){EventListMap::iterator iter = m_map.find(pEvent.mEventId);if(iter == m_map.end()){std::list<MyEntity*> entityList;entityList.push_front(pEntity);m_map.insert(EventListMap::value_type(pEvent.mEventId, entityList));}else{(iter->second).push_front(pEntity);}}void EventEngine::Detach(MyEvent pEvent){if(m_map.empty())return;EventListMap::iterator mIter = m_map.find(pEvent.mEventId);if(mIter != m_map.end()){EventListIter lIter = (mIter->second).begin();for(; lIter != (mIter->second).end(); ++lIter){if((*lIter)->getId() == pEvent.mUniqueIndex){(mIter->second).erase(lIter);return;}}}}void EventEngine::DetachAll(){EventListMapIter mIter  = m_map.begin();for(; mIter != m_map.end(); ++mIter ){mIter->second.clear();}m_map.clear();}void EventEngine::fire(MyEvent pEvent){EventListMapIter mIter = m_map.find(pEvent.mEventId);if(mIter == m_map.end())return;EventListIter lIter = (mIter->second).begin();for(; lIter != (mIter->second).end(); ++lIter){if((*lIter)->getId() == pEvent.mUniqueIndex){(*lIter)->CallBack();return;}}}int main(){Hero * h1 = new Hero(1);Hero * h2 = new Hero(2);Monster * m1 = new Monster(3);Monster * m2 = new Monster(4);LoginEvent l1(Event_Login, h1->getId());LoginEvent l2(Event_Logout, h1->getId());LogoutEvent l3(Event_Login, m1->getId());LogoutEvent l4(Event_Logout, m2->getId());h1->subscribe(l1);h1->subscribe(l2);m1->subscribe(l3);m1->subscribe(l4);fireEvent(l1);fireEvent(l3);system("pause");return 0;}

这里做了以下几个小改动。

1.将所有的观察者与被观察者放在一个EventEngine管理类统一管理。

2.全部观察者继承同一个接口,便于管理。

PS,本例中观察者为MyEntity以及实现该接口的全部子类,

被观察者为MyEvent以及实现该接口的全部子类。

EventEngine使用单例模式统一管理全部的注册事件的对象。EventEngine采用的容器是以Event事件ID作为Key,订阅该事件的全部观察者对象的list作为Value的map。

list是当订阅相同事件的对象注册事件时,直接把该对象插入list就可以了。

我们的采用EventId以及对象的Id联合的方式作为订阅该事件的对象的订阅标准。

既可以区分开不同的事件,也可以区分开不同的订阅该事件的订阅者。

全部的代码就是为了 fireEvent这一瞬间做准备。

如果还有什么疑问,最好自己写一遍代码,一切清楚。

0 0
原创粉丝点击