【LibUIDK界面庫系列文章】值改變時可發出通知的通用數據類型



作者:劉樹偉


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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章