POCO C++库学习和分析 -- 通知和事件 ( 二 )
2. 通知和事件的总览
2.1 相关类信息
下面是Poco库和通知、事件相关的类1) 同步通知实现:类Notification和NotificationCenter
2) 异步通知实现:类Notification和NotificationQueue
3) 事件 Events
2.2 概述
Poco文档上对于通知和事件的区别做了如下描述:1) 通知和事件是Poco库中支持的两种消息通知机制,目的是为了在源对象(source)发生某事件(something)时能够及时通知目的对象(target)
2) 使用Poco中的通知,必须注意通知对象(target)也可称观察者(observer)将无法得知事件源的情况。Poco::NotificationCenter和Poco::NotificationQueue是消息传递的中间载体,用来对源(source)和目标(target)进行解耦。
3) 如果对象(target)或者说观察者(observer)期望知道事件源的情况,或者想只从某一个确切的源接收事件,可以使用Poco::Event。Poco中的Event同时支持异步和同步消息。
看了上面的文档,千万不要以为通知无法获取源对象的信息。事实上,通过对代码的改写,我们也可以使通知支持上述功能。只不过通知是基于消息源角度的设计,在设计时,就认为对于通知者,关注重点并不在消息源,而在消息类型。关于这一点,可以看前面一篇文章 POCO C++库学习和分析 -- 通知和事件(一)。
下图说明了同步消息时,消息发送的流程:
3. 同步通知
3.1 消息
所有的通知类都继承自Poco::Notification,其定义如下:class Notification: public RefCountedObject
{
public:
typedef AutoPtr<Notification> Ptr;
Notification();
virtual std::string name() const;
protected:
virtual ~Notification();
};
从定义看,我们可以发现其从RefCountedObject类继承,也就是说其是一个引用计数对象。作为从RefCountedObject中继承的引用计数对象,毫无疑问在其在Poco中使用是和AutoPtr类配合的,完成堆对象的自动回收。关于AutoPtr的介绍,可以看前面的文章 POCO C++库学习和分析 -- 内存管理(一)。
使用时,我们可以从Notification继承,以便实现自己的通知,并且在通知类中可以放置我们想的任意数据,但是Poco中的Notification继承类不支持值语义操作,也就是说不支持拷贝构造函数(copy constructor)和赋值构造函数(assignment)。要注意的是这个限制并不是由于Notification继承类本身的限制导致的不支持,我们完全可以为其实现拷贝构造函数和赋值构造函数。这个限制是使用继承类的时候,为了实现动态对象的自动回收,消息的中介Poco::NotificationCenter和接收者Observer都使用了Poco::AutoPtr去传递和接收数据造成的。所以所有的Notification继承类对象都在堆上分配,运用时没有必要为其提供拷贝构造函数和赋值构造函数。
3.2 消息的发送者 source
类NotificationCenter类扮演了一个消息源的角色。下面是它的定义:class NotificationCenter
{
public:
NotificationCenter();
~NotificationCenter();
void addObserver(const AbstractObserver& observer);
void removeObserver(const AbstractObserver& observer);
void postNotification(Notification::Ptr pNotification);
bool hasObservers() const;
std::size_t countObservers() const;
static NotificationCenter& defaultCenter();
private:
typedef SharedPtr<AbstractObserver> AbstractObserverPtr;
typedef std::vector<AbstractObserverPtr> ObserverList;
ObserverList _observers;
mutable Mutex _mutex;
};
从定义可以看出它是一个目标对象的集合std::vector<SharedPtr<AbstractObserver>>_observers。
通过调用函数addObserver(const AbstractObserver& observer),可以完成目标对象的注册过程。调用函数removeObserver()则可以完成反注册。而函数postNotification是一个消息传递的过程,其定义如下:
void NotificationCenter::postNotification(Notification::Ptr pNotification)
{
poco_check_ptr (pNotification);
ScopedLockWithUnlock<Mutex> lock(_mutex);
ObserverList observersToNotify(_observers);
lock.unlock();
for (ObserverList::iterator it = observersToNotify.begin(); it !=
observersToNotify.end(); ++it)
{
(*it)->notify(pNotification);
}
}
从它的实现看,只是简单遍历_observers对象,并最终通过AbstractObserver->notify()把消息发送给通知对象。同时为了避免长时间占用_observers对象,在发送消息时,复制了一份。
当使用者调用postNotification函数时,毫无疑问,消息被触发。
3.3 消息的接收者 target
消息产生后,最终都要求被发送给合适的处理者。在C++中,处理者一定是一个对象,而处理即意味着行为,在C++中意味着类的成员函数,也就是说最终的处理要落实到类的对象实例的函数指针上。在Poco中,AbstractObserver可以理解成对象类的一个代理,它是一个纯虚类,定义了接收对象的接口。它的定义如下:class AbstractObserver
{
public:
AbstractObserver();
AbstractObserver(const AbstractObserver& observer);
virtual ~AbstractObserver();
AbstractObserver& operator = (const AbstractObserver& observer);
virtual void notify(Notification* pNf) const = 0;
virtual bool equals(const AbstractObserver& observer) const = 0;
virtual bool accepts(Notification* pNf) const = 0;
virtual AbstractObserver* clone() const = 0;
virtual void disable() = 0;
};
所有的接收者代理类都从类AbstractObserver继承,因为真实接收者的类类型未定,所以接受者代理类只能由模板技术去实现。这解决了处理时第一个问题。第二个问题是,不同的类处理函数可以不同。可以拥有一个,或多个参数,可以拥有或没有返回值。而编译器编译时,必须指定函数指针的类型。解决方法即把处理函数的类型固定下来。
在Poco库的内部实现Observer类和NObserver类中,其定义分别是:
void (C::*Callback)(N*);
void (C::*Callback)(const AutoPtr<N> &);
函数调用时分别带了一个参数。这其实解决了所有的问题,因为一个参数可以是结构体,供使用传入传出所需的值。当然我们也可以从AbstractObserver继承实现自己的目标代理类,这样我们可以定义自己所需要的函数类型。让我们来看一下Observer定义,NObserver的分析类似。
template <class C, class N>
class Observer: public AbstractObserver
{
public:
typedef void (C::*Callback)(N*);
Observer(C& object, Callback method):
_pObject(&object),
_method(method)
{
}
Observer(const Observer& observer):
AbstractObserver(observer),
_pObject(observer._pObject),
_method(observer._method)
{
}
~Observer()
{
}
Observer& operator = (const Observer& observer)
{
if (&observer != this)
{
_pObject = observer._pObject;
_method = observer._method;
}
return *this;
}
void notify(Notification* pNf) const
{
Poco::Mutex::ScopedLock lock(_mutex);
if (_pObject)
{
N* pCastNf = dynamic_cast<N*>(pNf);
if (pCastNf)
{
pCastNf->duplicate();
(_pObject->*_method)(pCastNf);
}
}
}
bool equals(const AbstractObserver& abstractObserver) const
{
const Observer* pObs = dynamic_cast<const Observer*>(&abstractObserver);
return pObs && pObs->_pObject == _pObject && pObs->_method == _method;
}
bool accepts(Notification* pNf) const
{
return dynamic_cast<N*>(pNf) != 0;
}
AbstractObserver* clone() const
{
return new Observer(*this);
}
void disable()
{
Poco::Mutex::ScopedLock lock(_mutex);
_pObject = 0;
}
private:
Observer();
C* _pObject;
Callback _method;
mutable Poco::Mutex _mutex;
};
Observer中存在一个类实例对象的指针_pObject,以及对应函数入口地址_method。其处理函数为notify。这里注意两点:
1. 使用了dynamic_cast转换,这意味着接受者处理的消息是向下继承的。如果一个对象订购了Poco::Notification,那么它将接受到所有继承自Poco::Notification的消息。
2. 调用了pCastNf->duplicate(),增加了引用计数,这意味着处理者在处理函数中必须相应的去调用pCastNf->release(),去释放引用计数。在这里我倒是没有搞明白,为什么要调用duplicate(),在我看来不调用也完全可以。可能是为了照顾引用计数对象的语义,即引用计数对象的所有权发生了改变,从NotificationCenter对象独占转变成为了真实处理类对象和NotificationCenter对象共同拥有所有权。
3.4 一个使用例子
#include "Poco/NotificationCenter.h"
#include "Poco/Notification.h"
#include "Poco/Observer.h"
#include "Poco/NObserver.h"
#include "Poco/AutoPtr.h"
#include <iostream>
using Poco::NotificationCenter;
using Poco::Notification;
using Poco::Observer;
using Poco::NObserver;
using Poco::AutoPtr;
class BaseNotification: public Notification
{
};
class SubNotification: public BaseNotification
{
};
class Target
{
public:
void handleBase(BaseNotification* pNf)
{
std::cout << "handleBase: " << pNf->name() << std::endl;
pNf->release(); // we got ownership, so we must release
}
void handleSub(const AutoPtr<SubNotification>& pNf)
{
std::cout << "handleSub: " << pNf->name() << std::endl;
}
};
int main(int argc, char** argv)
{
NotificationCenter nc;
Target target;
nc.addObserver(
Observer<Target, BaseNotification>(target, &Target::handleBase)
);
nc.addObserver(
NObserver<Target, SubNotification>(target, &Target::handleSub)
);
nc.postNotification(new BaseNotification);
nc.postNotification(new SubNotification);
nc.removeObserver(
Observer<Target, BaseNotification>(target, &Target::handleBase)
);
nc.removeObserver(
NObserver<Target, SubNotification>(target, &Target::handleSub)
);
return 0;
}
3.5 最后给出同步通知的类图

(版权所有,转载时请注明作者和出处 http://blog.youkuaiyun.com/arau_sh/article/details/8673459)