2012-05-02 121 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上。这是一个相对较新的设计,所以任何反馈都非常感谢。