2012-05-02 119 views
1

下面我試圖爲觀察者模式編寫一個sudo代碼。當觀察者希望觀察不同的項目時,實現觀察者模式

忽略語法錯誤。我想知道這是否是實施這個的正確方法。如果不是,請提出更好的方法。

// Used by the subject for keeping a track of what items the observer wants to observe 
typedef struct observerListStruct 
{ 
    bool getTemperatureUpdate; 
    bool getHumidityUpdate; 
    bool getPressureUpdate; 
    observer's-function pointer's address; 
}; 

// Subject's class 
class weatherData 
{ 
    public: 
     // Observers will call this function to register themselves. The function pointer will point to the function which will get called when updates are available. 
     void registerObservers (observer obj, observer's-FunctionPointer) 
     { 
      // This observer's function returns which items to observe. 
      char* f = obj.returnItemsToObserve(); 
      if f[0] = `1` 
       observerListStruct.getTemperatureUpdate = true; 
     } 

     void unregisterObservers (observer obj) {} 

    private: 
     vector <observerListStruct> observerList; 
     float temperature; 
     float humidity; 
     float pressure; 

     void notifyObservers()   {} 

     float getTemperature()   {} 
     float getHumidity()     {} 
     float getPressure()     {} 
} weatherDataObject; 

// Base class for observers containing common functions 
class observers 
{ 
    char ItemsToObserve [3] = {1, 2, 3}; 

    // This observer's function returns which items to observe. Default - return all items 
     virtual char* returnItemsToObserve() 
    { 
     return ItemsToObserve; 
    } 
}; 

class observerDisplayElementCurrentConditions : public observers 
{ 
    char ItemsToObserve [3] = {1, 2}; 

    char* returnItemsToObserve() 
    { 
     return ItemsToObserve; 
    } 

    // this function will be used as a function pointer for getting updates 
    void getUpdatesAndDisplayWeatherData (float, float) {} 
}; 
+0

'Subject'不應該知道哪個觀察者對哪個'Subject'屬性感興趣。每個'ConcreteObserver'都知道'ConcreteSubject'的屬性是什麼,'ConcreteSubject'應該有public getters,所以'ConcreteObserver'可以獲取這些屬性的最新值(當'ConcreteSubject'觸發事件或調用時'Subject :: Notify ()'方法 - Google for Gang Of Four Observer實現)。 'registerObservers'應該只是添加一個新的'ConcreteObserver'到Observers列表中。你可以用多個'ConcreteSubject'來註冊每個'ConcreteObserver'。 –

+0

@BojanKomazec「公共獲得者」是什麼意思?請解釋。當主題調用「notify」函數時,會發生什麼? –

+0

我的意思是公共存取方法。查看下面的AquilaRapax的答案,並查找'WheatherData :: getTemperature()','WheatherData :: getHumidity()'等''Notify()'遍歷所有註冊的觀察者列表並調用'Update()'。每個'ConcreteObserver'都實現'Update()',並且在這個方法內部通過這些getter獲得'ConcreteSubject'屬性的最新值。 –

回答

3

更多面向模式的解決方案(但沒有函數指針)可能如下。您可以參數化WeatherObserver-Class以僅獲取所需的值。

#include <list> 
#include <iostream> 

class Observable; //forward declaration 

//Base class for all observers 
class Observer { 
    friend class Observable; //allow access to observedSubject 

protected: 
    Observable *observedSubject; 

public: 
    virtual void update(){}; 
}; 


//Base class for all observables 
class Observable { 
private: 
    std::list<Observer * const> m_registeredObservers; 

public: 
    ~Observable() 
    { 
     //delete the observers 
     std::list<Observer * const>::iterator it = m_registeredObservers.begin(); 

     while (it != m_registeredObservers.end()) 
     { 
      delete *it; 
      it = m_registeredObservers.erase(it); 
     } 
    } 

    void addObserver(Observer * const _pObserver) 
    { 
     _pObserver->observedSubject = this; 
     m_registeredObservers.push_back(_pObserver); 
    } 

    void removeObserver(Observer * const _pObserver) 
    { 
     m_registeredObservers.remove(_pObserver); 
     delete _pObserver; 
    } 

    void notifyObservers() 
    { 
     std::list<Observer * const>::iterator it = m_registeredObservers.begin(); 

     while (it != m_registeredObservers.end()) 
     { 
      (*it)->update(); 
      it++; 
     } 
    } 
}; 

//Concrete Observable 
class WeatherData : public Observable { 
private: 
    float temperature; 
    float humidity; 
    float pressure; 

public: 
    WeatherData(): temperature(0), humidity(0), pressure(0) 
    {}; 

    float getTemperature() const 
    { 
     return temperature; 
    } 

    float getHumidity() const 
    { 
     return humidity; 
    } 

    float getPressure() const 
    { 
     return pressure; 
    } 

    void setTemperature(float _temperature) 
    { 
     if (temperature != _temperature) 
     { 
      temperature = _temperature; 
      notifyObservers(); 
     } 
    } 

    void setHumidity(float _humidity) 
    { 
     if (humidity != _humidity) 
     { 
      humidity = _humidity; 
      notifyObservers(); 
     } 
    } 

    void setPressure(float _pressure) 
    { 
     if (pressure != _pressure) 
     { 
      pressure = _pressure; 
      notifyObservers(); 
     } 
    } 

}; 


//Concrete implementation of an weather observer 
class WeatherObserver : public Observer 
{ 
    public: 
     WeatherObserver():Observer(){}; 
     void update() 
     { 
      WeatherData* pWeatherPtr = static_cast<WeatherData*>(observedSubject); 
      if (pWeatherPtr != 0) 
      { 
       float actHumidity = pWeatherPtr->getHumidity(); 
       float actPressure = pWeatherPtr->getPressure(); 
       float actTemperature = pWeatherPtr->getTemperature(); 

       //do something with the data 
       std::cout << "WeatherObserver update" << std::endl; 
       std::cout << "Temperature : " << actTemperature << std::endl; 
       std::cout << "Humidity : " << actHumidity << std::endl; 
       std::cout << "Pressure : " << actPressure << std::endl; 
      } 
     } 
}; 

int main() 
{ 
    WeatherData weatherData; 
    Observer * pObserver = new WeatherObserver(); 
    weatherData.addObserver(pObserver); 

    weatherData.setHumidity(100); 
    weatherData.setTemperature(100); 
} 
+0

這確實是一個非常體面的解決方案,但我會花一些時間來消化它。非常感謝。 –

+0

這個問題是否有效地要求它可以過濾觀察者通知,以便更改天氣數據的不同方面? – Pete

+0

一個小小的改正,你已經鑄造了一個'WheatherData *'的指針,你應該使用'pWheatherPtr'而不是再次投射指針,它也是更簡潔的代碼。 – Pupsik

2

我認爲定義一組每個觀察者都可以聽到的事件類型更容易,更具可擴展性。然後你註冊觀察者來監聽特定的事件類型。觀察到的然後保持爲每個事件註冊的觀察者列表,並且通知他們是否以及何時發生事件。使用std::function,std::bind(或boost等價物)的組合,很容易爲給定事件類型註冊回調。您可以將回調放入事件類型爲回調的映射中。

例如,沿着這些線路的東西(幾乎是僞代碼,還沒有經過測試)

class Publisher { 

public : 
    void subscribe(const std::string& event, 
       std::function<void(double)> callback) { 
    m_subscribers[s].push_back(callback);  
    } 
    void publish(const std::string& event) const { 
    for (auto& f : m_subscribers[event]) f(some double);} 

    void event(const std::string& event) const { publish(event);} 

private: 
    // map of event types (here simply strings) to list of callbacks 
    std::map<std::string&, 
      std::list<std::function<void(const std::string&)>>> m_subscribers; 
}; 

struct Foo { 
    void foo(double x) { 
    std::cout << "Foo received message: " << x << "\n"; 
    } 
}; 

struct Bar { 
    void bar(double x) { 
    std::cout << "Bar received message: " << x << "\n"; 
    } 
}; 

int main() { 
    Publisher pub; 
    Foo f0; 
    Foo f1; 
    Bar bar0; 

    pub.subscribe("RED", std::bind(&Foo::foo, &foo0, _1)); 
    pub.subscribe("GREEN", std::bind(&Foo::foo, &foo1, _1)); 
    pub.subscribe("WHITE", std::bind(&Foo::foo, &foo1, _1)); 
    pub.subscribe("RED", std::bind(&Bar::bar, &bar0, _1)); 
    pub.subscribe("BLUE", std::bind(&Bar::bar, &bar0, _1)); 
    pub.subscribe("MAGENTA", std::bind(&Bar::bar, &bar0, _1)); 

    // trigger a "GREEN" event 
    pub.event("GREEN"); 

} 

這裏,觀察者(或用戶)註冊到一些事件,通過串在這裏表示,他們註冊當這個事件發生時,回調被調用。在上面的例子中,我手動觸發一個事件來說明機制。

此事件回調機制允許從回調操作中分離實際項目。 Observed(或發佈者)知道爲給定事件傳遞迴調的哪個參數以及要調用哪些回調,因此觀察者不依賴於觀察對象的內部數據。

+0

請您介紹一下sudo代碼? –

+0

您的意思是:另一種方式可以實現一個全球表,其中將包含以下內容: 代碼: *** Observer |要觀察的項目|函數指針地址*** 觀察者將填滿此表,並且主題將從表中讀取並採取相應措施? 哪一個是更好的出路,爲什麼? –

+0

@AnishaKaul我做到了。我希望它能夠說明這個想法。 – juanchopanza

1
#include <algorithm> 
#include <vector> 


class WeatherFlags 
{ 
public: 
    WeatherFlags() 
     : mask_(0) 
    {} 
    union { 
     struct { 
      unsigned int temperature_ : 1; 
      unsigned int humidity_ : 1; 
      unsigned int pressure_ : 1; 
     }; 
     unsigned int mask_; 
    }; 
}; 

class WeatherData; 

class WeatherEvent 
{ 
public: 
    WeatherEvent(WeatherData* data, WeatherFlags const& flags) 
     : data_(data) 
     , flags_(flags) 
    {} 
    double getTemperature() const; 

    WeatherData* data_; 
    WeatherFlags flags_; 
}; 

class WeatherListener 
{ 
public: 
    virtual ~WeatherListener() = 0; 
    virtual void onWeatherUpdate(WeatherEvent& e) = 0; 
}; 
inline WeatherListener::~WeatherListener() {} 

class WeatherListenerEntry 
{ 
public: 
    WeatherListenerEntry() 
     : listener_(0) 
    {} 
    WeatherListenerEntry(WeatherListener* listener, WeatherFlags const& flags) 
     : listener_(listener) 
     , flags_(flags) 
    {} 

    WeatherListener* listener_; 
    WeatherFlags flags_; 
}; 

class WeatherData 
{ 
public: 
    WeatherData(); 
    void addListener(WeatherListener* listener, WeatherFlags const& flags); 
    void removeListener(WeatherListener* listener); 

    void notify(WeatherFlags const& flags); 

    double getTemperature() const { return temperature_; } 
private: 
    typedef std::vector<WeatherListenerEntry> Listeners; 
    Listeners listeners_; 
    double temperature_; 
}; 

WeatherData::WeatherData() 
: temperature_(0) 
{} 

void WeatherData::addListener(WeatherListener* listener, WeatherFlags const& flags) 
{ 
    // TODO Could maybe check for the addition of duplicates here... 
    listeners_.push_back(WeatherListenerEntry(listener, flags)); 
} 

void WeatherData::removeListener(WeatherListener* listener) 
{ 
    struct ListenerEquals { 
     WeatherListener* listener_; 
     ListenerEquals(WeatherListener* listener) 
      : listener_(listener) 
     {} 
     bool operator()(WeatherListenerEntry const& e) const { 
      return (e.listener_ == listener_); 
     } 
    }; 
    listeners_.erase(
     std::remove_if(listeners_.begin(), listeners_.end(), ListenerEquals(listener)), 
     listeners_.end()); 
} 

void WeatherData::notify(WeatherFlags const& flags) 
{ 
    WeatherEvent evt(this, flags); 
    for (Listeners::iterator i = listeners_.begin(); i != listeners_.end(); ++i) 
    { 
     if (0 != (i->flags_.mask_ & flags.mask_)) { 
      i->listener_->onWeatherUpdate(evt); 
     } 
    } 
} 

double 
WeatherEvent::getTemperature() const 
{ 
    return data_->getTemperature(); 
} 


#include <iostream> 
class WeatherObserverStdout : public WeatherListener 
{ 
public: 
    void observe(WeatherData& data) { 
     WeatherFlags flags; 
     flags.temperature_ = true; // interested in temperature only. 
     data.addListener(this, flags);   
    } 
    virtual void onWeatherUpdate(WeatherEvent& e); 
}; 

void 
WeatherObserverStdout::onWeatherUpdate(WeatherEvent& e) 
{ 
    double temp = e.getTemperature(); 
    std::cout << "Temperatrure: " << temp << std::endl; 
} 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    WeatherData wdata; 
    WeatherObserverStdout obs; 
    obs.observe(wdata); 

    WeatherFlags flags; 
    wdata.notify(flags); 
    flags.temperature_ = true; 
    wdata.notify(flags); 
    return 0; 
} 
+1

請注意,這是不完整的,但它處理通知的過濾,在WeatherData類中,當值更改時,必須使用適當的標誌調用notify。 – Pete

1

我的兩分錢......

經典(四人幫)實現觀察者模式的通知上的更改觀察員對象的任何財產。在你的問題中,你想註冊觀察者到特定的屬性,而不是作爲一個整體的主題。您可以將觀察者模式向下移動一級,並將屬性作爲具體主題並定義其觀察者(每個屬性),但有一種更好的方法可以解決此問題。

在C#中觀察者模式是通過事件代表實現的。代表表示事件處理程序 - 當事件被觸發時應該執行的函數。代表可以添加(註冊)或從事件中刪除(未註冊)。

在C++中,仿函數充當代表 - 它們可以存儲所有必要的信息以在不同的上下文中調用某個全局函數或類方法。事件是(註冊)仿函數的集合,當事件被提出(調用)時,它基本上遍歷該列表並調用所有仿函數(參見juanchopanza解決方案中的Publisher::publish方法)。

我試圖實現C++版本的事件和委託,並在修改後的觀察者模式中使用它們,可以應用在你的情況。這是我想出了:

#include <list> 
#include <iostream> 
#include <algorithm> 

// use base class to resolve the problem of how to put into collection objects of different types 
template <typename TPropertyType> 
struct PropertyChangedDelegateBase 
{ 
    virtual ~PropertyChangedDelegateBase(){}; 
    virtual void operator()(const TPropertyType& t) = 0; 
}; 

template <typename THandlerOwner, typename TPropertyType> 
struct PropertyChangedDelegate : public PropertyChangedDelegateBase<TPropertyType> 
{ 
    THandlerOwner* pHandlerOwner_; 

    typedef void (THandlerOwner::*TPropertyChangeHandler)(const TPropertyType&); 
    TPropertyChangeHandler handler_; 

public: 
    PropertyChangedDelegate(THandlerOwner* pHandlerOwner, TPropertyChangeHandler handler) : 
     pHandlerOwner_(pHandlerOwner), handler_(handler){} 

    void operator()(const TPropertyType& t) 
    { 
     (pHandlerOwner_->*handler_)(t); 
    } 
}; 

template<typename TPropertyType> 
class PropertyChangedEvent 
{ 
public: 
    virtual ~PropertyChangedEvent(){}; 

    void add(PropertyChangedDelegateBase<TPropertyType>* const d) 
    { 
     std::list<PropertyChangedDelegateBase<TPropertyType>* const>::const_iterator it = std::find(observers_.begin(), observers_.end(), d); 
     if(it != observers_.end()) 
      throw std::runtime_error("Observer already registered"); 

     observers_.push_back(d); 
    } 


    void remove(PropertyChangedDelegateBase<TPropertyType>* const d) 
    {  
     std::list<PropertyChangedDelegateBase<TPropertyType>* const>::const_iterator it = std::find(observers_.begin(), observers_.end(), d); 
     if(it != observers_.end()) 
      observers_.remove(d); 
    } 

    // notify 
    void operator()(const TPropertyType& newValue) 
    { 
     std::list<PropertyChangedDelegateBase<TPropertyType>* const>::const_iterator it = observers_.begin(); 
     for(; it != observers_.end(); ++it) 
     { 
      (*it)->operator()(newValue); 
     } 
    } 

protected: 
    std::list<PropertyChangedDelegateBase<TPropertyType>* const> observers_; 
}; 

// class that owns concrete subjects 
class PropertyOwner1 
{ 
    int property1_; 
    float property2_; 
public: 
    PropertyChangedEvent<int> property1ChangedEvent; 
    PropertyChangedEvent<float> property2ChangedEvent; 

    PropertyOwner1() : 
     property1_(0), 
     property2_(0.0f) 
    {} 

    int property1() const {return property1_;} 
    void property1(int n) 
    { 
     if(property1_ != n) 
     { 
      property1_ = n; 
      std::cout << "PropertyOwner1::property1(): property1_ set to " << property1_ << std::endl; 
      property1ChangedEvent(property1_); 
     } 
    } 

    float property2() const {return property2_;} 
    void property2(float n) 
    { 
     if(property2_ != n) 
     { 
      property2_ = n; 
      std::cout << "PropertyOwner1::property2(): property2_ set to " << property2_ << std::endl; 
      property2ChangedEvent(property2_); 
     } 
    } 
}; 

// class that owns concrete subjects 
class PropertyOwner2 
{ 
    bool property1_; 
    double property2_; 
public: 
    PropertyChangedEvent<bool> property1ChangedEvent; 
    PropertyChangedEvent<double> property2ChangedEvent; 

    PropertyOwner2() : 
     property1_(false), 
     property2_(0.0) 
    {} 

    bool property1() const {return property1_;} 
    void property1(bool n) 
    { 
     if(property1_ != n) 
     { 
      property1_ = n; 
      std::cout << "PropertyOwner2::property1(): property1_ set to " << property1_ << std::endl; 
      property1ChangedEvent(property1_); 
     } 
    } 

    double property2() const {return property2_;} 
    void property2(double n) 
    { 
     if(property2_ != n) 
     { 
      property2_ = n; 
      std::cout << "PropertyOwner2::property2(): property2_ set to " << property2_ << std::endl; 
      property2ChangedEvent(property2_); 
     } 
    } 
}; 

// class that observes changes in property1 of PropertyOwner1 and property1 of PropertyOwner2 
struct PropertyObserver1 
{ 
    void OnPropertyOwner1Property1Changed(const int& newValue) 
    { 
     std::cout << "\tPropertyObserver1::OnPropertyOwner1Property1Changed(): \n\tnew value is: " << newValue << std::endl; 
    } 

    void OnPropertyOwner2Property1Changed(const bool& newValue) 
    { 
     std::cout << "\tPropertyObserver1::OnPropertyOwner2Property1Changed(): \n\tnew value is: " << newValue << std::endl; 
    } 
}; 

// class that observes changes in property2 of PropertyOwner1 and property2 of PropertyOwner2 
struct PropertyObserver2 
{ 
    void OnPropertyOwner1Property2Changed(const float& newValue) 
    { 
     std::cout << "\tPropertyObserver2::OnPropertyOwner1Property2Changed(): \n\tnew value is: " << newValue << std::endl; 
    } 

    void OnPropertyOwner2Property2Changed(const double& newValue) 
    { 
     std::cout << "\tPropertyObserver2::OnPropertyOwner2Property2Changed(): \n\tnew value is: " << newValue << std::endl; 
    } 
}; 

int main(int argc, char** argv) 
{ 
    PropertyOwner1 propertyOwner1; 
    PropertyOwner2 propertyOwner2;  

    PropertyObserver1 propertyObserver1; 
    PropertyObserver2 propertyObserver2; 

    // register observers 
    PropertyChangedDelegate<PropertyObserver1, int> delegate1(&propertyObserver1, &PropertyObserver1::OnPropertyOwner1Property1Changed); 
    propertyOwner1.property1ChangedEvent.add(&delegate1); 

    PropertyChangedDelegate<PropertyObserver2, float> delegate2(&propertyObserver2, &PropertyObserver2::OnPropertyOwner1Property2Changed); 
    propertyOwner1.property2ChangedEvent.add(&delegate2); 

    PropertyChangedDelegate<PropertyObserver1, bool> delegate3(&propertyObserver1, &PropertyObserver1::OnPropertyOwner2Property1Changed); 
    propertyOwner2.property1ChangedEvent.add(&delegate3); 

    PropertyChangedDelegate<PropertyObserver2, double> delegate4(&propertyObserver2, &PropertyObserver2::OnPropertyOwner2Property2Changed); 
    propertyOwner2.property2ChangedEvent.add(&delegate4); 

    propertyOwner1.property1(1); 
    propertyOwner1.property2(1.2f); 

    propertyOwner2.property1(true); 
    propertyOwner2.property2(3.4); 

    // unregister PropertyObserver1 
    propertyOwner1.property1ChangedEvent.remove(&delegate1); 
    propertyOwner2.property1ChangedEvent.remove(&delegate3); 

    propertyOwner1.property1(2); 
    propertyOwner1.property2(4.5f); 
} 

輸出:

PropertyOwner1::property1(): property1_ set to 1 
     PropertyObserver1::OnPropertyOwner1Property1Changed(): 
     new value is: 1 
    PropertyOwner1::property2(): property2_ set to 1.2 
     PropertyObserver2::OnPropertyOwner1Property2Changed(): 
     new value is: 1.2 
    PropertyOwner2::property1(): property1_ set to 1 
     PropertyObserver1::OnPropertyOwner2Property1Changed(): 
     new value is: 1 
    PropertyOwner2::property2(): property2_ set to 3.4 
     PropertyObserver2::OnPropertyOwner2Property2Changed(): 
     new value is: 3.4 
    PropertyOwner1::property1(): property1_ set to 2 
    PropertyOwner1::property2(): property2_ set to 4.5 
     PropertyObserver2::OnPropertyOwner1Property2Changed(): 
     new value is: 4.5 

每個觀察者與特定財產登記和通知時,每個觀察者知道究竟是誰的主人財產和財產的新價值。

2

我寫了很多C++代碼,需要爲我正在開發的一些遊戲組件創建一個Observer。我需要一些東西來分發「開始框架」,「用戶輸入」等,作爲遊戲中的事件給感興趣的各方。

我也希望能夠處理更多事件的粒度。我有很多小的事情會發生......我不需要讓那些有興趣重新設置下一幀的部分擔心用戶輸入的更改。我也希望它是直的C++,不依賴於平臺或特定的技術(如boost,Qt等),因爲我經常構建和重用不同的組件(以及它們背後的想法)項目。

這裏是什麼,我想出了一個草圖作爲一種解決方案:

  1. 觀察員與鍵(枚舉值,而不是字符串單身,這是一個速度的權衡,因爲密鑰是不搜索散列,但它意味着沒有簡單的「字符串」名稱,並且您必須提前定義它們)以便主題註冊感興趣。因爲它是單例,它總是存在。
  2. 每個主題都來自一個共同的基類。基類有一個抽象的虛函數Notify(...),它必須在派生類中實現,還有一個析構函數,當它被刪除時,它會從Observer中移除它(它總是可以到達)。
  3. 在Observer本身內部,如果在通知(...)正在進行時調用Detach(...),則任何分離的主題最終都會列在列表中。
  4. 當通知(...)在觀察者上被調用時,它創建主題列表的臨時副本。當它遍歷它時,它將它與最近分離的進行比較。如果目標不在上面,則在目標上調用Notify(...)。否則,它會被跳過。
  5. Observer中的Notify(...)還會跟蹤處理級聯調用的深度(A通知B,C,D和D.Notify(...)觸發Notify(...)調用以E等)

這是接口告終什麼看起來像:

/* 
The Notifier is a singleton implementation of the Subject/Observer design 
pattern. Any class/instance which wishes to participate as an observer 
of an event can derive from the Notified base class and register itself 
with the Notiifer for enumerated events. 

Notifier derived classes MUST implement the notify function, which has 
a prototype of: 

void Notify(const NOTIFIED_EVENT_TYPE_T& event) 

This is a data object passed from the Notifier class. The structure 
passed has a void* in it. There is no illusion of type safety here 
and it is the responsibility of the user to ensure it is cast properly. 
In most cases, it will be "NULL". 

Classes derived from Notified do not need to deregister (though it may 
be a good idea to do so) as the base class destructor will attempt to 
remove itself from the Notifier system automatically. 

The event type is an enumeration and not a string as it is in many 
"generic" notification systems. In practical use, this is for a closed 
application where the messages will be known at compile time. This allows 
us to increase the speed of the delivery by NOT having a 
dictionary keyed lookup mechanism. Some loss of generality is implied 
by this. 

This class/system is NOT thread safe, but could be made so with some 
mutex wrappers. It is safe to call Attach/Detach as a consequence 
of calling Notify(...). 

*/ 


class Notified; 

class Notifier : public SingletonDynamic<Notifier> 
{ 
public: 
    typedef enum 
    { 
     NE_MIN = 0, 
     NE_DEBUG_BUTTON_PRESSED = NE_MIN, 
     NE_DEBUG_LINE_DRAW_ADD_LINE_PIXELS, 
     NE_DEBUG_TOGGLE_VISIBILITY, 
     NE_DEBUG_MESSAGE, 
     NE_RESET_DRAW_CYCLE, 
     NE_VIEWPORT_CHANGED, 
     NE_MAX, 
    } NOTIFIED_EVENT_TYPE_T; 

private: 
    typedef vector<NOTIFIED_EVENT_TYPE_T> NOTIFIED_EVENT_TYPE_VECTOR_T; 

    typedef map<Notified*,NOTIFIED_EVENT_TYPE_VECTOR_T> NOTIFIED_MAP_T; 
    typedef map<Notified*,NOTIFIED_EVENT_TYPE_VECTOR_T>::iterator NOTIFIED_MAP_ITER_T; 

    typedef vector<Notified*> NOTIFIED_VECTOR_T; 
    typedef vector<NOTIFIED_VECTOR_T> NOTIFIED_VECTOR_VECTOR_T; 

    NOTIFIED_MAP_T _notifiedMap; 
    NOTIFIED_VECTOR_VECTOR_T _notifiedVector; 
    NOTIFIED_MAP_ITER_T _mapIter; 

    // This vector keeps a temporary list of observers that have completely 
    // detached since the current "Notify(...)" operation began. This is 
    // to handle the problem where a Notified instance has called Detach(...) 
    // because of a Notify(...) call. The removed instance could be a dead 
    // pointer, so don't try to talk to it. 
    vector<Notified*> _detached; 
    int32 _notifyDepth; 

    void RemoveEvent(NOTIFIED_EVENT_TYPE_VECTOR_T& orgEventTypes, NOTIFIED_EVENT_TYPE_T eventType); 
    void RemoveNotified(NOTIFIED_VECTOR_T& orgNotified, Notified* observer); 

public: 

    virtual void Reset(); 
    virtual bool Init() { Reset(); return true; } 
    virtual void Shutdown() { Reset(); } 

    void Attach(Notified* observer, NOTIFIED_EVENT_TYPE_T eventType); 
    // Detach for a specific event 
    void Detach(Notified* observer, NOTIFIED_EVENT_TYPE_T eventType); 
    // Detach for ALL events 
    void Detach(Notified* observer); 

    /* The design of this interface is very specific. I could 
    * create a class to hold all the event data and then the 
    * method would just have take that object. But then I would 
    * have to search for every place in the code that created an 
    * object to be used and make sure it updated the passed in 
    * object when a member is added to it. This way, a break 
    * occurs at compile time that must be addressed. 
    */ 
    void Notify(NOTIFIED_EVENT_TYPE_T, const void* eventData = NULL); 

    /* Used for CPPUnit. Could create a Mock...maybe...but this seems 
    * like it will get the job done with minimal fuss. For now. 
    */ 
    // Return all events that this object is registered for. 
    vector<NOTIFIED_EVENT_TYPE_T> GetEvents(Notified* observer); 
    // Return all objects registered for this event. 
    vector<Notified*> GetNotified(NOTIFIED_EVENT_TYPE_T event); 
}; 

/* This is the base class for anything that can receive notifications. 
*/ 
class Notified 
{ 
public: 
    virtual void Notify(Notifier::NOTIFIED_EVENT_TYPE_T eventType, const void* eventData) = 0; 
    virtual ~Notified(); 

}; 

typedef Notifier::NOTIFIED_EVENT_TYPE_T NOTIFIED_EVENT_TYPE_T; 

注:公告類有一個單一的功能,通知(......)在這裏。因爲void *的不是類型安全的,我創建的其他版本,其中相同的通知長相:

virtual void Notify(Notifier::NOTIFIED_EVENT_TYPE_T eventType, int value); 
virtual void Notify(Notifier::NOTIFIED_EVENT_TYPE_T eventType, const string& str); 

相應的通知(...)方法添加到通知本身。所有這些都使用單個函數來獲取「目標列表」,然後在目標上調用相應的函數。這很好地工作,並保持接收器不必做醜陋的演員。

這似乎工作得很好。該解決方案與源代碼一起發佈在網絡here上。這是一個相對較新的設計,所以任何反饋都非常感謝。