boost中使用signal/slot实现了回调功能,也就是经典的发布订阅模式。我们这里采用模版来完成这个功能,设计类图如下:
发布者Publisher管理信号msignal,对外暴露set接口,外部调用set时触发成员对象msignal动作。
msignal通过动态数组管理信号槽slot,提供connect与operator()两个接口,connect将监听者挂载到一个slot上,operator()会依次调用slot上订阅者的回调函数。
源码如下:
#include <vector>
#include <iostream>
template<typename T, typename T1>
class slot
{
public:
slot(T* pObj,void (T::*pMemberFunc)(T1))
{
m_pObj=pObj;
m_pMemberFunc=pMemberFunc;
}
void Execute(T1 para)
{
(m_pObj->*m_pMemberFunc)(para);
}
private:
T* m_pObj;
void (T::*m_pMemberFunc)(T1);
};
template<typename T, typename T1>
class msignal
{
public:
void connect(T* pObj,void (T::*pMemberFunc)(T1 para))
{
m_slots.push_back(new slot<T,T1>(pObj,pMemberFunc));
}
~msignal()
{
for (auto ite=m_slots.begin();ite!=m_slots.end();ite++)
{
delete *ite;
}
}
void operator()(T1 para)
{
for (auto ite=m_slots.begin();ite!=m_slots.end();ite++)
{
(*ite)->Execute(para);
}
}
private:
std::vector<slot<T,T1>* > m_slots;
};
class Subscriber
{
public:
void callback1(int a)
{
std::cout<<"cb1: "<<a<<std::endl;
}
void callback2(int a)
{
std::cout<<"cb2: "<<a<<std::endl;
}
};
template<typename T, typename T1>
class Publisher
{
public:
Publisher(): m_value(0) {}
int get_value()
{
return m_value;
}
void set_value(int new_value)
{
if (new_value!=m_value)
{
m_value=new_value;
m_sig(new_value);
}
}
msignal<T,T1> m_sig;
private:
int m_value;
};
int main(int argc,char** arg)
{
Subscriber r1;
Subscriber r2;
Publisher<Subscriber,int> s;
s.m_sig.connect(&r1,&Subscriber::callback1);
s.m_sig.connect(&r1,&Subscriber::callback2);
s.m_sig.connect(&r2,&Subscriber::callback1);
s.set_value(1);
return 0;
}
上一版实现有个局限性
上一版实现有个局限性,就是发布者Publisher在实例化的时候就需要知道Subscriber的类型,且只能通知单类型的通知,改进办法是将订阅者类型推导延迟到调用connect时,实现如下:
#include <iostream>
#include <vector>
#include <functional>
#include <algorithm>
class A{
public:
void Clicked(int id)
{
std::cout << "A::Clicked" << std::endl;
}
};
class B{
public:
void Clicked(int id)
{
std::cout << "B::Clicked" << std::endl;
}
};
class SlotBase{
public:
virtual void OnSignal(int id) = 0;
};
template<class T>
class Slot : public SlotBase{
public:
typedef void (T::*MemberFuncType)(int);
Slot(T* obj, MemberFuncType func)
:obj_(obj), func_(func)
{
}
virtual void OnSignal(int id)
{
(obj_->*func_)(id);
}
private:
T* obj_;
MemberFuncType func_;
};
template<class T>
SlotBase* MakeSlot(T * p, void (T::*func)(int))
{
return new Slot<T>( p, func ) ;
}
class Signal
{
public:
virtual ~Signal()
{
std::for_each(slots_.begin(), slots_.end(), SignalDelete());
}
struct SignalDelete: public std::unary_function<SlotBase*, void>
{
public:
void operator()(SlotBase*& slot) const
{
delete slot;
}
};
struct SignalAction: public std::binary_function<SlotBase*, int, void>
{
public:
void operator()(SlotBase*& slot, const int& i) const
{
slot->OnSignal(i);
}
};
void signal(int id)
{
std::for_each(slots_.begin(), slots_.end(), bind2nd(SignalAction(), id));
}
void connect( SlotBase* s)
{
slots_.push_back(s);
}
protected:
std::vector< SlotBase* > slots_;
};
int main()
{
A a;
B b;
Signal signal;
signal.connect(MakeSlot(&a, &A::Clicked));
signal.connect(MakeSlot(&b, &B::Clicked));
signal.signal(1);
return 0;
}
可以看到,signal可以挂载不同类型的监听者。