一个观察者模式可复用的实现:
现在我们有了所有的元素了我们需要去创建观察者模式可复用的实现。除此之外我还为观察者模式的原始结构添加了一些扩展来使其更加适用于现实的项目。
1. 我将IObserver声明为ISubject的子集(这里是嵌套类)。这是有意义的,是因为一个目标对象和它的观察者接口之间有紧密的联系。换句话来说:目标对象定义了它的观察者接口。
2. Notify()函数调用接收单个参数并且有一个返回值。参数以及返回值的类型都是由声明于ISubject里的模板传递的。这取决于你怎么处理这些参数。我经常使用它们来传递关于目标对象状态改变的信息。这就是总所周知的观察者模式的推动改变。
3. 另外一个模板声明被用来存储在观察者的目标对象列表中与观察者有关的一个属性。这个属性的语义是在事物实现者的职责之内。例如:它能够作为一个事件标示或者开端来传递给观察者它们所真真关心的那些事件。
4. 所有的这些类和接口都是在命名空间tool下声明。
上面的扩展对于避免不必要的方法请求很有用,这一点对于分布式计算的场景尤为重要。
ISubject与IObserver接口
template<class OBSERVER_INFO = int,
class RETURN = int, class ARG1 = int>
struct ISubject
{
// type of observer related data
typedef typename OBSERVER_INFO observer_info_t;
// return type of the callback fn
typedef typename RETURN return_t;
// type of the callback fn argument
typedef typename ARG1 arg1_t;
// Classes that act as observer have
// to implement this interface
struct IObserver
{
typedef typename RETURN return_t;
typedef typename ARG1 arg1_t;
virtual return_t Update( arg1_t ) = 0;
};
virtual bool AttachObserver(IObserver* pObserver,
observer_info_t Info) = 0;
virtual bool DetachObserver(IObserver* pObserver) = 0;
};
如上所述,事物接口ISubject 获得两个模板参数来参数化ISubject::IObserver::Update()的签名。第三个模板参数用来声明存储在每个观察者中信息的属性类型。到目前为止就没有什么其他的了。现在让我们看看ISubject的可复用实现吧:
template<class OBSERVER_INFO = int,
class RETURN = int, class ARG1 = int>
class SimpleSubject : public ISubject<OBSERVER_INFO,
RETURN, ARG1>
{
public:
virtual bool AttachObserver(IObserver* pObserver,
observer_info_t Info = observer_info_t() )
{
m_aObservers.push_back(observer_entry_t(pObserver,
Info));
return true;
}
class RETURN = int, class ARG1 = int>
class SimpleSubject : public ISubject<OBSERVER_INFO,
RETURN, ARG1>
{
public:
virtual bool AttachObserver(IObserver* pObserver,
observer_info_t Info = observer_info_t() )
{
m_aObservers.push_back(observer_entry_t(pObserver,
Info));
return true;
}
virtual bool DetachObserver( IObserver* pObserver )
{
clients_t::iterator i = m_aObservers.begin();
for( ; i != m_aObservers.end(); ++i )
if( i->pObserver == pObserver ) {
m_aObservers.erase( i );
return true;
}
return false;
}
{
clients_t::iterator i = m_aObservers.begin();
for( ; i != m_aObservers.end(); ++i )
if( i->pObserver == pObserver ) {
m_aObservers.erase( i );
return true;
}
return false;
}
virtual void NotifyObserver( arg1_t arg1 = arg1_t() )
{
for( clients_t::iterator i = m_aObservers.begin();
i != m_aObservers.end(); ++i )
i->pObserver->Update( arg1 );
}
{
for( clients_t::iterator i = m_aObservers.begin();
i != m_aObservers.end(); ++i )
i->pObserver->Update( arg1 );
}
protected:
struct observer_entry_t
{
observer_entry_t( IObserver* o, observer_info_t i )
: pObserver( o ), Info( i ) {}
IObserver* pObserver;
observer_info_t Info;
};
typedef std::vector< observer_entry_t > clients_t;
clients_t m_aObservers;
};
{
observer_entry_t( IObserver* o, observer_info_t i )
: pObserver( o ), Info( i ) {}
IObserver* pObserver;
observer_info_t Info;
};
typedef std::vector< observer_entry_t > clients_t;
clients_t m_aObservers;
};
SimpleSubject类提供了一个IObserver接口的简单实现,并且使用了一个std::vector来存储observer_entry_t元素。每个元素代表了带有它的属性的附着的观察者。这个例子展示出你怎样才能利用覆写SimpleObserver::NotifyObserver来使用这个属性。
ObserverAdapter类
template<class OUTER, class SUBJECT>
struct ObserverAdapter : public SUBJECT::IObserver
{
// type of outer class
typedef typename OUTER outer_t;
// returntype of callback fn
typedef typename SUBJECT::return_t return_t;
// type of callback fn argument
typedef typename SUBJECT::arg1_t arg1_t;
// type of a pointer to member fn in class outer
typedef return_t (OUTER::*mfunc_t)(arg1_t);
struct ObserverAdapter : public SUBJECT::IObserver
{
// type of outer class
typedef typename OUTER outer_t;
// returntype of callback fn
typedef typename SUBJECT::return_t return_t;
// type of callback fn argument
typedef typename SUBJECT::arg1_t arg1_t;
// type of a pointer to member fn in class outer
typedef return_t (OUTER::*mfunc_t)(arg1_t);
ObserverAdapter( outer_t* pObj = NULL,
mfunc_t pmfUpdate = NULL )
: m_pObj( pObj ),
m_pmfUpdate( pmfUpdate ) {}
mfunc_t pmfUpdate = NULL )
: m_pObj( pObj ),
m_pmfUpdate( pmfUpdate ) {}
virtual return_t Update( arg1_t arg1 = arg1_t() )
{
return (m_pObj->*m_pmfUpdate)( arg1 );
}
{
return (m_pObj->*m_pmfUpdate)( arg1 );
}
virtual void SetUpdate( outer_t* pObj,
mfunc_t pmfUpdate )
{
m_pObj = pObj;
m_pmfUpdate = pmfUpdate;
}
mfunc_t pmfUpdate )
{
m_pObj = pObj;
m_pmfUpdate = pmfUpdate;
}
outer_t* m_pObj;
mfunc_t m_pmfUpdate;
mfunc_t m_pmfUpdate;
};
这个显然是最令人感兴趣的类了,它只有了了数行。它获得两个模板参数:外部类的类型以及目标对象接口。外部类的类型需要声明成函数指针类型m_func_t,目标对象接口被用作派生自IObserver接口来获得恰当的return_t(返回值)和arg1_t(声明)类型。使用这个回调可以被传递到构造函数或者被之后的SetUpdate()方法改变。两个同时都获得两个参数:你想处理的对象实例,以及要调用的回调函数和成员函数的地址。成员函数的原型必须是以[virtual]return_t func(arg1_t)的形式。(成员函数是不是被声明为虚都无所谓,虚拟不是成员函数签名的一部分)。每当目标对象调用Update()的时候,它真正的工作都被委派给了回调函数。存储的成员函数地址m_pmfUpdates根据存储的对象m_pObj以及传入的给定参数arg1来调用。
总结:
设计模式对于开发人员来讲的确很棒,它将开发人员重复发明轮子的苦海中解脱出来,并且帮助他们关注于高层次的设计抽象。但是,虽然我们不必发明轮子,但是我们不得不每次都重建它,因为在可复用设计与可复用代码之前存在着横沟。这个横沟往往在本地实现是常常引起的制约他们现实项目的应用的结构性依赖中体现出来。这些不好的结构性依赖都与继承的使用有关。我们能够利用聚合以及使用委派技术来替换继承从而解决这个问题。整合了成员函数回调,这是个非常有弹性的方法,它允许我们建立插件式(可插入式)的成分以及通用特性,而不用对使用他们的环境做任何假设。