首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

觀察者模式 C++ 通用實作(二):樣板化主題與參數完美轉發

     編輯:關於C++

為消除重複樣板碼,將觀察者模式抽象為通用庫。本文從介面基底類別出發,逐步引入樣板技術、Traits 輔助類別與標籤分派,實現支援任意觀察者介面、任意參數個數與型別、完美保留參數傳遞語意(含引用語意)的 BasicSubject。

從介面基底到樣板化

早期版本需定義 IObserver、Event、BasicSubject 三個基底類別,強制觀察者實作單一 update(Event&)。改用樣板後,BasicSubject 直接接受觀察者型別作為樣板參數,無需共同基底:

template <class ObserverT, class ContainerT = std::list<ObserverT*>>
class BasicSubject {
public:
    inline void addObserver(ObserverT& o) { observers_.push_back(&o); }
    inline void removeObserver(ObserverT& o) { /* erase */ }
protected:
    ContainerT observers_;
};

notifyAll 參數轉發難題與解法

notifyAll 需將參數轉發給觀察者的多種更新方法(0~5 參數)。直接使用樣板參數推導會遇到:

  • 實參與形參型別不符(如 int vs int&)導致推導模稜兩可。
  • 常數實參(如 10)無法繫結至非 const 引用參數。
  • 需保留觀察者方法原始參數傳遞語意(值傳遞、引用、const 引用)。

解法:定義 arg_type_traits 依目的函數參數型別決定轉發函數參數型別:

template <typename T> struct arg_type_traits { typedef const T& result; };
template <typename T> struct arg_type_traits<T&> { typedef T& result; };
template <typename T> struct arg_type_traits<const T&> { typedef const T& result; };

配合多重載 notifyAll(0~5 參數),實現完美轉發:

template <typename ReturnT, typename Arg1T>
inline void notifyAll(ReturnT (ObserverT::*pfn)(Arg1T),
    typename arg_type_traits<Arg1T>::result arg1) {
    for (auto it = observers_.begin(); it != observers_.end(); ++it)
        ((*it)->*pfn)(arg1);
}
// 同理擴充至 2~5 參數版本

使用範例

struct MyObserver {
    void copy(int src, int& dest) { dest = src; }
};
struct MySubject : public BasicSubject<MyObserver> {
    void trigger() {
        int i = 0;
        notifyAll(&MyObserver::copy, 100, i); // i 成為 100
    }
};

客戶端程式碼簡潔直觀,無需記憶輔助類別(如 ByRef),編譯器自動根據觀察者方法簽名選擇正確轉發語意。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved