POCO C++库学习和分析 -- 通知和事件 ( 二 )
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://img-my.csdn.net/uploads/201303/14/1363247118_2192.jpg)
(版权所有,转载时请注明作者和出处 http://blog.csdn.net/arau_sh/article/details/8673459)