您的位置:首页 > 编程语言 > C语言/C++

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

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

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

举个例子:老板就是被观察者,员工就是观察者,大家都看着老板的指挥行动,让你往东你就往东,让你往西你就往西,员工在老板处留下了不同的联系方式有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; //多个事件的订阅者列表,结构体作为Key
typedef 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这一瞬间做准备。

如果还有什么疑问,最好自己写一遍代码,一切清楚。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: