【问题标题】:Implementing Observer pattern when observers wish to observe different items当观察者希望观察不同的项目时实现观察者模式
【发布时间】:2012-05-11 16:53:44
【问题描述】:

当观察者希望观察不同的项目时,我尝试为观察者模式编写 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) {}
};

【问题讨论】:

  • Subject 不应该知道哪个观察者对Subject 的哪个属性感兴趣。每个ConcreteObserver 都知道它感兴趣的ConcreteSubject 的属性。ConcreteSubject 应该有公共getter,所以ConcreteObserver 可以获取这些属性的最新值(当ConcreteSubject 触发事件或调用Subject::Notify() 方法时 - Google for Gang Of Four Observer 实施)。 registerObservers 应该只是将新的ConcreteObserver 添加到观察者列表中。每个ConcreteObserver可以注册多个ConcreteSubjects。
  • @BojanKomazec 你所说的“公共吸气剂”是什么意思?请解释。当主体调用“通知”函数时,会发生什么?
  • 我的意思是公共访问器方法。请参阅下面的 AquilaRapax 的答案并查找 WheatherData::getTemperature()WheatherData::getHumidity() 等。Notify() 遍历所有已注册观察者的列表并在它们上调用 Update()。每个ConcreteObserver 都实现了Update(),在这个方法中它通过这些getter 获取ConcreteSubject 属性的最新值。
  • 很高兴它有帮助。也许我应该发布一些代码......它更容易理解。我可能会这样做,以供参考。
  • @BojanKomazec 你说:“主体不应该知道哪个观察者对哪个主体的属性感兴趣”。但是,如果我们谈论报纸出版商(他们是主题)——他们难道不会[跟踪哪些报纸应该发送给哪个订阅者?

标签: c++ oop design-patterns observer-pattern


【解决方案1】:

一个更面向模式的解决方案(但没有函数指针)可能如下。您可以对 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);
}

【讨论】:

  • 这确实是一个非常不错的解决方案,但我需要一些时间来消化它。非常感谢。
  • 这个问题是否有效地要求可以过滤观察者通知以针对天气数据的不同方面发生变化?
  • 一个小修正,您已经将指针投射到WheatherData*,您应该使用pWheatherPtr 而不是再次投射指针,这样代码也会更简洁。
  • @Pupsik 您能否澄清一下,“再次投射指针”是什么意思?你是说 WeatherObserver::update-method 吗?
  • @AquilaRapax 此评论与更新的代码无关。它与旧代码相关(您可以查看 history)。
【解决方案2】:

我编写了很多 C++ 代码,并且需要为我正在开发的一些游戏组件创建一个 Observer。我需要一些东西来将“帧开始”、“用户输入”等作为游戏中的事件分发给感兴趣的各方。

我还希望可以处理的事件更细化。我有很多小事情发生了......我不需要让有兴趣为下一帧重置的部分担心用户输入的变化。

我还希望它是纯 C++,不依赖于平台或特定技术(如 boost、Qt 等),因为我经常在不同的项目中构建和重用组件(以及它们背后的想法) .

这是我想出的解决方案的粗略草图:

  1. Observer 是一个带有键的单例(枚举值,而不是字符串;这是一种速度权衡,因为键没有经过散列搜索,但这意味着没有简单的“字符串”名称,您必须提前定义它们)要注册感兴趣的主题。因为它是单例,所以它始终存在。
  2. 每个主题都派生自一个公共基类。基类有一个抽象虚函数 Notify(...),它必须在派生类中实现,以及一个析构函数,当它被删除时,它会从 Observer 中移除(它总是可以到达)。
  3. 在 Observer 本身内部,如果在 Notify(...) 正在进行时调用 Detach(...),则任何已分离的 Subjects 最终都会出现在列表中。
  4. 当在 Observer 上调用 Notify(...) 时,它会创建主题列表的临时副本。当它迭代它时,它将它与最近分离的进行比较。如果目标不在其上,则在目标上调用 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;

注意:Notified 类只有一个函数,此处为 Notify(...)。因为 void* 不是类型安全的,所以我创建了其他版本,其中 notify 如下所示:

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

相应的 Notify(...) 方法已添加到通知程序本身。所有这些都使用一个函数来获取“目标列表”,然后在目标上调用适当的函数。这很好用,并且使接收器不必进行难看的演员表。

这似乎运作良好。该解决方案与源代码一起发布在网络here 上。这是一个相对较新的设计,因此非常感谢任何反馈。

【讨论】:

    【解决方案3】:

    我的两分钱...

    观察者模式的经典(四人组)实现通知观察者主题的任何属性的变化。在您的问题中,您希望将观察者注册到 特定 属性,而不是整个主题。您可以将观察者模式下移一级,将属性作为具体的主题并定义它们的观察者(每个属性),但是有一种更好的方法可以解决这个问题。

    在 C# 中,观察者模式是通过 eventsdelegates 实现的。委托代表事件处理程序 - 触发事件时应执行的函数。可以从事件中添加(注册)或删除(未注册)代表。

    在 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
    

    每个观察者都注册了一个特定的属性,当收到通知时,每个观察者都知道谁是该属性的所有者以及该属性的新值是什么。

    【讨论】:

      【解决方案4】:

      我认为定义一组每个观察者都可以收听的事件类型更容易,也更具可扩展性。然后你注册观察者来监听那个特定的事件类型。然后被观察者保留为每个事件注册的观察者列表,并在事件发生时通知他们。使用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(或发布者)知道为给定事件传递回调的参数,以及调用哪些回调,因此观察者不依赖于被观察对象的内部数据。

      【讨论】:

      • 您的意思是:另一种方法是实现一个包含以下内容的全局表:代码:Observer |观察项目 |函数指针地址 观察者会填满这个表,主体会从表中读取并采取相应的行动吗?哪一个是更好的出路,为什么?
      • @AnishaKaul 我做到了。我希望它能说明这个想法。
      • @AnishaKaul Observer 不会主动做任何事情,Observed 知道何时调用 Observers 注册函数(我们将其抽象为函数对象回调以增加灵活性)
      • @AnishaKaul 我希望它有用。请注意,观察者和观察者完全解耦为类。他们不需要了解彼此的任何事情。这可能是非常有利的。
      【解决方案5】:
      #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;
      }
      

      【讨论】:

      • 请注意,这是不完整的,但它确实处理通知过滤,在 WeatherData 类中,当值更改时,您必须使用适当的标志调用 notify。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-02-20
      • 2023-04-10
      • 1970-01-01
      • 2011-12-21
      • 1970-01-01
      • 2022-12-14
      相关资源
      最近更新 更多