现在的位置: 首页 > 综合 > 正文

观察者模式(Observer)

2012年07月27日 ⁄ 综合 ⁄ 共 9378字 ⁄ 字号 评论关闭

【Head First Design Pattern】在对象之间定义一对多的依赖,这样以来,当一个对象改变状态,依赖它的对象都会收到通知,并自动更新。

通俗概念:
在被观察者中存放着观察者的信息,一旦有改变,马上调用观察者的方法。
比如,你手机(被观察者)的通讯录保存着"张三李四王五"(观察者)的手机号码,"张李王"也要保存你的手机号码。1.你一旦有什么事情,就会主动打电话通知"张李王"。2.如果张三不想收到你的通知,可以打电话给你,叫你把他从通讯录删除,下次他就收不到你的通知。3.如果张三还需要你的通知,可以打电话让你把他的手机号码添加到通讯录,下次就可以收到通知。这例子不一定恰当,但有一点是:被观察者必须手持观察者的联系方式,同时观察者最好也要有被观察者的联系方式。

C++实现代码:

第一个例子:

ObserverPattern.h:

#pragma once
#include <iostream>
#include <list>

using namespace std;

class CAbstractObserver
{
public:
    virtual void update(float temp, float humidity, float pressure) = 0;
};

class CAbstractDisplayElement
{
public:
    virtual void display() = 0;
};

class CAbstractSubject
{
public:
    virtual void registerObserver(CAbstractObserver *pObserver) = 0;
    virtual void removeObserver(CAbstractObserver *pObserver) = 0;
    virtual void notifyObservers() = 0;
};

class CWeatherData : public CAbstractSubject
{
public:
    CWeatherData()
    {
    }
    virtual void registerObserver(CAbstractObserver *pObserver)
    {
        m_ObserverList.push_back(pObserver);
    }
    virtual void removeObserver(CAbstractObserver *pObserver)
    {
        m_ObserverList.remove(pObserver);
    }
    virtual void notifyObservers()
    {
        CAbstractObserver *pObserver;
        for (list<CAbstractObserver *>::iterator it = m_ObserverList.begin(); it != m_ObserverList.end(); ++it)
        {
            pObserver = *it;
            pObserver->update(m_fTemperature, m_fHumidity, m_fPressure);
        }
    }
    void measurementsChanged()
    {
        notifyObservers();
    }
    void setMeasurements(float temp, float humidity, float pressure)
    {
        m_fTemperature = temp;
        m_fHumidity = humidity;
        m_fPressure = pressure;
        measurementsChanged();
    }

private:
    list<CAbstractObserver *> m_ObserverList;
    float m_fTemperature;
    float m_fHumidity;
    float m_fPressure;
};

class CCurrentConditionsDisplay : public CAbstractObserver, public CAbstractDisplayElement
{
public:
    CCurrentConditionsDisplay(CAbstractSubject *pWeatherData)
    {
        m_pWeatherData = pWeatherData;
        m_pWeatherData->registerObserver(this);
    }
    virtual void update(float temp,float humidity,float pressure)
    {
        m_fTemperature = temp;
        m_fHumidity = humidity;
        display();
    }
    virtual void display()
    {
        cout << "Current conditions: " << m_fTemperature << "F degrees and " << m_fHumidity << "% humidity" << endl;
    }

private:
    float m_fTemperature;
    float m_fHumidity;
    CAbstractSubject *m_pWeatherData;
};

class CStatisticsDisplay : public CAbstractObserver, public CAbstractDisplayElement
{
public:
    CStatisticsDisplay(CWeatherData *pWeatherData)
    {
        m_fMaxTemp = 0.0f;
        m_fMinTemp = 200;
        m_fTempSum = 0.0f;
        m_iNumReadings = 0;
        pWeatherData->registerObserver(this);
    }
    virtual void update(float temp,float humidity,float pressure)
    {
        m_fTempSum += temp;
        m_iNumReadings++;

        if (temp > m_fMaxTemp)
        {
            m_fMaxTemp = temp;
        }
        if (temp < m_fMinTemp)
        {
            m_fMinTemp = temp;
        }
        display();
    }
    virtual void display()
    {
        cout << "Avg/Max/Min temperature = " << (m_fTempSum / m_iNumReadings) << '/' << m_fMaxTemp << '/' << m_fMinTemp << endl;
    }

private:
    float m_fMaxTemp;
    float m_fMinTemp;
    float m_fTempSum;
    int   m_iNumReadings;
};

class CForecastDisplay : public CAbstractObserver, public CAbstractDisplayElement
{
public:
    CForecastDisplay(CWeatherData *pWeatherData)
    {
        m_fCurrentPressure = 29.92f;
        pWeatherData->registerObserver(this);
    }
    virtual void update(float temp,float humidity,float pressure)
    {
        m_fLastPressure = m_fCurrentPressure;
        m_fCurrentPressure = pressure;

        display();
    }
    virtual void display()
    {
        cout << "Forecast: ";
        if (m_fCurrentPressure > m_fLastPressure)
        {
            cout << "Improving weather on the way!" << endl;
        }
        else if (m_fCurrentPressure == m_fLastPressure)
        {
            cout << "More of the same" << endl;
        }
        else if (m_fCurrentPressure < m_fLastPressure)
        {
            cout << "Watch out for cooler, rainy weather" << endl;
        }
    }
    
private:
    float m_fCurrentPressure;
    float m_fLastPressure;
};

class CHeatIndexDisplay : public CAbstractObserver, public CAbstractDisplayElement
{
public:
    CHeatIndexDisplay(CWeatherData *pWeatherData)
    {
        m_fHeatIndex = 0.0f;
        pWeatherData->registerObserver(this);
    }
    virtual void update(float temp,float humidity,float pressure)
    {
        m_fHeatIndex = computerHeatIndex(temp, humidity);
        display();
    }
    virtual void display()
    {
        cout << "Heat index is " << m_fHeatIndex << endl;
    }
    
private:
    float computerHeatIndex(float t, float rh)
    {
        float index = (float) ((16.923 + (0.185212 * t) + (5.37941 * rh)
				- (0.100254 * t * rh) + (0.00941695 * (t * t))
				+ (0.00728898 * (rh * rh)) + (0.000345372 * (t * t * rh))
				- (0.000814971 * (t * rh * rh))
				+ (0.0000102102 * (t * t * rh * rh))
				- (0.000038646 * (t * t * t)) + (0.0000291583 * (rh * rh * rh))
				+ (0.00000142721 * (t * t * t * rh))
				+ (0.000000197483 * (t * rh * rh * rh))
				- (0.0000000218429 * (t * t * t * rh * rh)) + 0.000000000843296 * (t
				* t * rh * rh * rh)) - (0.0000000000481975 * (t * t * t * rh
				* rh * rh)));
		return index;
    }

private:
    float m_fHeatIndex;
};


ObserverPattern.cpp:

#include "ObserverPattern.h"

int main()
{
    CWeatherData weatherData;
    
    CCurrentConditionsDisplay currentDisplay(&weatherData);
    CStatisticsDisplay statisticsDisplay(&weatherData);
    CForecastDisplay forecastDisplay(&weatherData);
    CHeatIndexDisplay heatIndexDisplay(&weatherData);

    weatherData.setMeasurements(80, 65, 30.4f);
    weatherData.setMeasurements(82, 70, 29.2f);
    weatherData.setMeasurements(78, 90, 29.2f);

    return 0;
}

第二个例子(模仿Java本身的观察者模式):

ObserverPatternJ.h:

#pragma once
#include <iostream>
#include <list>

using namespace std;

class CAbstractObserver
{
public:
    virtual void update(CAbstractObservable *pObservable, void *arg) = 0;
};

class CAbstractDisplayElement
{
public:
    virtual void display() = 0;
};

class CAbstractObservable
{
public:
    virtual void addObserver(CAbstractObserver *pObserver) = 0;
    virtual void deleteObserver(CAbstractObserver *pObserver) = 0;
    virtual void notifyObservers() = 0;
};

class CWeatherData : public CAbstractObservable
{
public:
    CWeatherData()
    {
        m_bChanged = false;
    }
    virtual void addObserver(CAbstractObserver *pObserver)
    {
        m_ObserverList.push_back(pObserver);
    }
    virtual void deleteObserver(CAbstractObserver *pObserver)
    {
        m_ObserverList.remove(pObserver);
    }
    virtual void notifyObservers()
    {
        if (m_bChanged)
        {
            CAbstractObserver *pObserver;
            for (list<CAbstractObserver *>::iterator it = m_ObserverList.begin(); it != m_ObserverList.end(); ++it)
            {
                pObserver = *it;
                pObserver->update(m_fTemperature, m_fHumidity, m_fPressure);
            }
        }
        m_bChanged = false;
    }
    void measurementsChanged()
    {
        setChanged();
        notifyObservers();
    }
    void setMeasurements(float temp, float humidity, float pressure)
    {
        m_fTemperature = temp;
        m_fHumidity = humidity;
        m_fPressure = pressure;
        measurementsChanged();
    }
    float getTemperature()
    {
        return m_fTemperature;
    }
    float getHumidity()
    {
        return m_fHumidity;
    }
    float getPressure()
    {
        return m_fPressure;
    }
    void setChanged()
    {
        m_bChanged = true;
    }
    void clearChanged()
    {
        m_bChanged = false;
    }
    bool hasChanged()
    {
        return m_bChanged;
    }

private:
    list<CAbstractObserver *> m_ObserverList;
    float m_fTemperature;
    float m_fHumidity;
    float m_fPressure;
    bool  m_bChanged;
};

class CCurrentConditionsDisplay : public CAbstractObserver, public CAbstractDisplayElement
{
public:
    CCurrentConditionsDisplay(CAbstractObservable *pObservable)
    {
        m_pObservable = pObservable;
        m_pObservable->addObserver(this);
    }
    virtual void update(CAbstractObservable * pObservable,void * arg)
    {
        CWeatherData *pWeatherData = dynamic_cast<CWeatherData *>(pObservable);
        if (pWeatherData)
        {
            m_fTemperature = pWeatherData->getTemperature();
            m_fHumidity = pWeatherData->getHumidity();
            display();
        }
    }
    virtual void display()
    {
        cout << "Current conditions: " << m_fTemperature << "F degrees and " << m_fHumidity << "% humidity" << endl;
    }

private:
    float m_fTemperature;
    float m_fHumidity;
    CAbstractObservable *m_pObservable;
};

class CStatisticsDisplay : public CAbstractObserver, public CAbstractDisplayElement
{
public:
    CStatisticsDisplay(CWeatherData *pObservable)
    {
        m_fMaxTemp = 0.0f;
        m_fMinTemp = 200;
        m_fTempSum = 0.0f;
        m_iNumReadings = 0;
        m_pObservable = pObservable;
        m_pObservable->addObserver(this);
    }
    virtual void update(CAbstractObservable * pObservable,void * arg)
    {
        CWeatherData *pWeatherData = dynamic_cast<CWeatherData *>(pObservable);
        if (pWeatherData)
        {
            int temp = pWeatherData->getTemperature();
            
            m_fTempSum += temp;
            m_iNumReadings++;
            if (temp > m_fMaxTemp)
            {
                m_fMaxTemp = temp;
            }
            if (temp < m_fMinTemp)
            {
                m_fMinTemp = temp;
            }
            display();
        }
    }
    virtual void display()
    {
        cout << "Avg/Max/Min temperature = " << (m_fTempSum / m_iNumReadings) << '/' << m_fMaxTemp << '/' << m_fMinTemp << endl;
    }

private:
    float m_fMaxTemp;
    float m_fMinTemp;
    float m_fTempSum;
    int   m_iNumReadings;
    CAbstractObservable *m_pObservable;
};

class CForecastDisplay : public CAbstractObserver, public CAbstractDisplayElement
{
public:
    CForecastDisplay(CWeatherData *pObservable)
    {
        m_fCurrentPressure = 29.92f;
        m_pObservable = pObservable;
        m_pObservable->addObserver(this);
    }
    virtual void update(CAbstractObservable * pObservable,void * arg)
    {
        CWeatherData *pWeatherData = dynamic_cast<CWeatherData *>(pObservable);
        if (pWeatherData)
        {
            m_fLastPressure = m_fCurrentPressure;
            m_fCurrentPressure = pWeatherData->getPressure();
        }
    }
    virtual void display()
    {
        cout << "Forecast: ";
        if (m_fCurrentPressure > m_fLastPressure)
        {
            cout << "Improving weather on the way!" << endl;
        }
        else if (m_fCurrentPressure == m_fLastPressure)
        {
            cout << "More of the same" << endl;
        }
        else if (m_fCurrentPressure < m_fLastPressure)
        {
            cout << "Watch out for cooler, rainy weather" << endl;
        }
    }
    
private:
    float m_fCurrentPressure;
    float m_fLastPressure;
    CAbstractObservable *m_pObservable;
};

class CHeatIndexDisplay : public CAbstractObserver, public CAbstractDisplayElement
{
public:
    CHeatIndexDisplay(CWeatherData *pObservable)
    {
        m_fHeatIndex = 0.0f;
        m_pObservable = pObservable;
        m_pObservable->addObserver(this);
    }
    virtual void update(CAbstractObservable * pObservable,void * arg)
    {
        CWeatherData *pWeatherData = dynamic_cast<CWeatherData *>(pObservable);
        if (pWeatherData)
        {
            m_fHeatIndex = computerHeatIndex(pWeatherData->getTemperature(), pWeatherData->getHumidity());
            display();
        }
    }
    virtual void display()
    {
        cout << "Heat index is " << m_fHeatIndex << endl;
    }
    
private:
    float computerHeatIndex(float t, float rh)
    {
        float index = (float) ((16.923 + (0.185212 * t) + (5.37941 * rh)
				- (0.100254 * t * rh) + (0.00941695 * (t * t))
				+ (0.00728898 * (rh * rh)) + (0.000345372 * (t * t * rh))
				- (0.000814971 * (t * rh * rh))
				+ (0.0000102102 * (t * t * rh * rh))
				- (0.000038646 * (t * t * t)) + (0.0000291583 * (rh * rh * rh))
				+ (0.00000142721 * (t * t * t * rh))
				+ (0.000000197483 * (t * rh * rh * rh))
				- (0.0000000218429 * (t * t * t * rh * rh)) + 0.000000000843296 * (t
				* t * rh * rh * rh)) - (0.0000000000481975 * (t * t * t * rh
				* rh * rh)));
		return index;
    }

private:
    float m_fHeatIndex;
    CAbstractObservable *m_pObservable;
};


ObserverPatternJ.cpp:

与ObserverPattern.cpp一样

抱歉!评论已关闭.