作者:刘树伟
我们设想这样一种应用: 一类整形数据int a, 有10个窗口A1 --- A10会显示它, 另有一个窗口B可编辑它. 当B编辑完a后, 窗口A1 --- A10需要刷新a, 这是典型的"文档/视"模型, 用MFC类CDocument和CView来实现是这样的: CDocument的派生类保存a, 把窗口A1到A10和窗口B作成CView的派生类, 通过CDocument::AddView把这11个窗口加到文档中, 当窗口B修改a以后, 调用CDocument::UpdateAllViews刷新所有视图, 来更新a. 这是一种常用的方法, 但有些弊端, 首先如果显示a的窗口不是CView的派生类, 将得不到刷新的机会; 其次B修改A后, 要主动调用UpdateAllViews, 才能刷新所有视图; 最后, 如果没有文档/视支持, 将不能使用这种方法, 比如控制台程序, 或者使用其它应用程序框架创建的程序.
我们可以抽象"文档/视"这一结构, 使a变化的时候, 自动通知需要得知这一变化的对象, 而不用关心是谁修改了a, 并且被通知的对象也不局限CView的派生类对象.
一个对象被修改, 通常是执行"="操作, 我们可以通过C++的模板类, 包装任意类型的对象, 然后重载"="操作符, 当通过调用operator=来修改a的值时, 在operator=中我们可以通过回调函数的方法, 来执行用户传入的回调函数. 而这些回调函数, 正是用户需要在a改变时, 执行的函数.
以下是抽象后的C++类模板.
template<typename T>
class CNotifyType
{
public:
typedef int (*CNotifyTypeChanged)(const T &rOld, const T &rNew, long lParam);
CNotifyType()
{
}
CNotifyType(const CNotifyType &v)
{
_value = v;
}
~CNotifyType()
{
}
const CNotifyType& operator=(const T& v)
{
T old = _value;
_value = v;
if (old != _value)
{
int nSize = m_vf.size();
for (int i=0; i<nSize; ++i)
{
pair<CNotifyTypeChanged, long> *f = &m_vf[i];
if (f->first != NULL)
(f->first)(old, _value, f->second);
}
}
return *this;
}
operator T () const
{
return _value;
}
void RegisterNotifyCallback(CNotifyTypeChanged f, long lParam)
{
pair<CNotifyTypeChanged, long> p(f, lParam);
m_vf.push_back(p);
}
void UnregisterNotifyCallback(CNotifyTypeChanged f)
{
int nSize = m_vf.size();
for (int i=0; i<nSize; ++i)
{
pair<CNotifyTypeChanged, long> *f = &m_vf[i];
if (f->first != f)
{
m_vf.erase(m_vf.back() + i);
break;
}
}
}
private:
T _value;
vector<pair<CNotifyTypeChanged, long> > m_vf;
};
// example
int Notify(const CString &rOld, const CString &rNew, long lParam)
{
ASSERT(lParam == 123);
return 0;
}
int Notify2(const CString &rOld, const CString &rNew, long lParam)
{
ASSERT(lParam == 456);
return 0;
}
CNotifyType<CString> a;
a.RegisterNotifyCallback(Notify, 123); // 相当于AddView
a.RegisterNotifyCallback(Notify2, 456);
a = _T("abc"); // auto call Notify and Notify2 // 内部相当于调用UpdateAllViews