作者:劉樹偉
我們設想這樣一種應用: 一類整形數據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