2012-05-02 5 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'はpublic getterを持っていなければなりません。 'ConcreteObserver'はそれらのプロパティの最新値を取得できます(' ConcreteSubject'がイベントをトリガーするとき、 ()メソッド - Gang Of Four Observer実装用のGoogle)。 'registerObservers'はオブザーバのリストに新しい' ConcreteObserver'を追加するだけです。それぞれの 'ConcreteObserver'を複数の' ConcreteSubject'で登録することができます。 –

+0

@BojanKomazec「公共ゲッター」とはどういう意味ですか?説明してください。サブジェクトが「通知」機能を呼び出すと、何が起こるはずですか? –

+0

私はパブリックアクセサメソッドを意味します。下記のAquilaRapaxの答えを見て、 'WheatherData :: getTemperature()'、 'WheatherData :: getHumidity()'などを探してください。 'Notify()'は登録されているすべてのオブザーバのリストを調べ、その上で 'Update()'を呼び出します。それぞれの 'ConcreteObserver'は' Update() 'を実装し、このメソッドの中でそれらのゲッターを通して' ConcreteSubject'のプロパティの最新の値を取得します。 –

答えて

3

より多くのパターン指向の解決策(ただし関数ポインタなし)は次のようになります。 WeatherObserver-Classをパラメータ化して、必要な値だけを取得することができます。 Observerパターンの

#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

小規模の修正が1つあります。すでに 'WheatherData *'へのポインタをキャストしています。ポインタを再キャスティングする代わりに 'pWheatherPtr'を使うべきです。また、よりクリーンなコードになります。 – Pupsik

2

各観測者が聞くことができるイベントタイプのセットを定義する方が簡単でスケーラブルであると思います。その後、オブザーバーを登録してその特定のイベントタイプを聞きます。観測された観測者は、各イベントごとに登録されたオブザーバーのリストを保持し、イベントが発生した場合に通知します。 std::functionstd::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

あなたは意味ですか:別の方法は、次を含むグローバルテーブルを実装することです: コード: ***オブザーバー|観察されるべき項目|ファンクションポインタアドレス*** オブザーバがこのテーブルを埋めるので、被験者はテーブルから読み出してそれに応じて行動しますか? どちらが良い方法ですか、そしてなぜですか? –

+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クラスでは値の変更時に適切なフラグで通知する必要があるため、通知のフィルタリングを処理します。 – Pete

1

私の2セント...

クラシック(四人組)の実装は、対象のいかなる性質の変化にオブザーバを通知します。あなたの質問では、の特定のプロパティにオブザーバーを登録したいと思っています。 Observerパターンを1レベル下に移動して、プロパティを具体的なテーマとして取り込み、オブザーバーを(プロパティごとに)定義することができますが、この問題を解決する方法が1つあります。

In C#Observerパターンは、イベントの代理人によって実装されています。デリゲートは、イベントハンドラを表します。イベントハンドラは、イベントが発生したときに実行する必要がある機能です。代理人は、イベントから追加(登録)または削除(登録解除)することができます。

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++コードを書いており、私が取り組んでいたゲームコンポーネントのオブザーバーを作成する必要がありました。ゲームのイベントとして「フレームの開始」、「ユーザー入力」などを関係者に配布するために何かが必要でした。

私はまた、処理できるイベントでより細分化したいと思っていました。私は少しでも多くのことをしています...私は、次のフレームのためにユーザー入力の変更を心配するためにリセットに興味のある部分を持つ必要はありません。

また、コンポーネントや(その背後にあるアイデア)を別々に作成して再利用するため、プラットフォームや特定の技術(ブースト、Qtなど)に依存しない、プロジェクト。ここで

は私が解決策として思い付いたのラフスケッチです:

  1. オブザーバーは、キー(列挙値ではなく、文字列とシングルトンであり、キーが検索されないので、これは、速度のトレードオフであります簡単な "文字列"の名前を意味するものではなく、事前にそれらを定義しなければならないということを意味します)。これはシングルトンなので、常に存在します。
  2. 各サブジェクトは共通の基本クラスから派生しています。基本クラスには、派生クラスで実装する必要がある抽象仮想関数Notify(...)と、削除時にObserver(常に到達可能)から削除するデストラクタがあります。
  3. Observer自体の内部で、Notify(...)が進行中にDetach(...)が呼び出された場合、分離されたサブジェクトはリストになります。
  4. ObserverでNotify(...)が呼び出されると、Subjectリストの一時コピーが作成されます。それを反復するにつれて、それは最近分離されたものと比較されます。ターゲットがその上にない場合、ターゲットに対してNotify(...)が呼び出されます。それ以外の場合はスキップされます。
  5. ObserverのNotify(...)は、カスケードコールを処理するための深さを追跡します(AはB、C、DおよびD.Notify(...)トリガーにNotify

    /* 
    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; 
    

    注:Eなど)

にこれは、インターフェイスがのように見えることになったものです届出クラスは、単一の機能を有しており、ここでは)...(通知。 (...)メソッドは、通知自体に追加された通知対応

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

ます。void *はタイプセーフされていないので、私のようなルックスに通知他のバージョンを作成しました。これらはすべて、「ターゲットリスト」を取得してターゲット上の適切な関数と呼ばれる単一の関数を使用していました。これはうまく動作し、レシーバーが醜いキャストをする必要がなくなります。

これはうまくいくようです。このソリューションは、ソースコードとともにウェブhereに掲載されています。これは比較的新しいデザインなので、どんなフィードバックも高く評価されています。

関連する問題