1. 程式人生 > >《Head First 設計模式》例子的C++實現(2 觀察者模式)

《Head First 設計模式》例子的C++實現(2 觀察者模式)

最近在學習設計模式,用的是 《Head First 設計模式》這本書。感覺這本書寫的還是很不錯的,深入淺出的介紹了各種常用的設計模式。唯一有點不方便的地方是這本書的例子全都是用的 Java 來實現的。而我主要是用 C++。所以就動手將書上的程式碼用 C++ 來實現了一遍。

觀察者模式

首先是三個介面的程式碼:

//observer.h
#ifndef OBSERVER_H
#define OBSERVER_H


class Observer
{
public:
    Observer() {}
    virtual void update(double temp, double
humidity, double pressure) = 0; }; #endif // OBSERVER_H
//Subject.h
#ifndef SUBJECT_H
#define SUBJECT_H

#include "observer.h"
#include <QList>
class Subject
{
public:
    Subject();
    void registerObserver(Observer *o);
    void removeObserver(Observer *o);
    void notifyObservers();
private
: QList<Observer *> m_list; protected: double m_temp; double m_humidity; double m_pressure; }; #endif // SUBJECT_H
#include "subject.h"

Subject::Subject()
{

}

void Subject::registerObserver(Observer *o)
{
    m_list.append(o);
}

void Subject::removeObserver(Observer *o)
{
    int i =
m_list.indexOf(o); if( i >= 0 ) { m_list.removeAt(i); } } void Subject::notifyObservers() { foreach (Observer * o, m_list) { o->update(m_temp, m_humidity, m_pressure); } }
#ifndef DISPLAYELEMENT_H
#define DISPLAYELEMENT_H


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

#endif // DISPLAYELEMENT_H

下面是 WeatherData 類:

#ifndef WEATHERDATA_H
#define WEATHERDATA_H

#include "subject.h"

class WeatherData : public Subject
{
public:
    WeatherData();
    void setMeasurements(double t, double h, double p);
    void measurementsChanged();
};

#endif // WEATHERDATA_H
#include "weatherdata.h"

WeatherData::WeatherData()
{

}

void WeatherData::setMeasurements(double t, double h, double p)
{
   m_temp = t;
   m_humidity = h;
   m_pressure = p;
   measurementsChanged();
}

void WeatherData::measurementsChanged()
{
    notifyObservers();
}

最後是四種觀察者:

#include "observer.h"
#include "displayelement.h"

class CurrentConditionDisplay : public Observer, public DisplayElement
{
public:
    CurrentConditionDisplay();
    void display() override;
    void update(double temp, double humidity, double pressure) override;
private:
    double m_temp;
    double m_humidity;
    double m_pressure;
};

class ForecastDisplay: public Observer, public DisplayElement
{
public:
    ForecastDisplay();
    void display() override;
    void update(double temp, double humidity, double pressure) override;
private:
    double currentPressure = 29.92f;
    double lastPressure;
};

class HeatIndexDisplay : public Observer, public DisplayElement
{
public:
    HeatIndexDisplay();
    void display() override;
    void update(double temp, double humidity, double pressure) override;
private:
    double m_heatIndex;
    double computeHeatIndex(double t, double rh);
};

class StatisticsDisplay: public Observer, public DisplayElement
{
public:
    StatisticsDisplay();
    void display() override;
    void update(double temp, double humidity, double pressure) override;
private:
    int m_numReadings;
    double m_tempSum;
    double m_maxTemp;
    double m_minTemp;
};
#include "display.h"
#include <iostream>

using std::cout;
using std::endl;

CurrentConditionDisplay::CurrentConditionDisplay()
{

}

void CurrentConditionDisplay::display()
{
    cout << "Current conditions:"  << m_temp
         << " F degrees and " << m_humidity
         << "% humidity" << endl;
}

void CurrentConditionDisplay::update(double temp, double humidity, double pressure)
{
    m_temp = temp;
    m_humidity = humidity;
    m_pressure = pressure;
    display();
}

ForecastDisplay::ForecastDisplay()
{

}

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

void ForecastDisplay::update(double temp, double humidity, double pressure)
{
    lastPressure = currentPressure;
    currentPressure = pressure;
    display();
}

HeatIndexDisplay::HeatIndexDisplay()
{

}

void HeatIndexDisplay::display()
{
    cout << "Heat index is " << m_heatIndex << endl;
}

void HeatIndexDisplay::update(double temp, double humidity, double pressure)
{
    (void) pressure;
    m_heatIndex = computeHeatIndex(temp, humidity);
    display();
}

double HeatIndexDisplay::computeHeatIndex(double t, double rh)
{
    double index = (double)((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;
}

StatisticsDisplay::StatisticsDisplay():
    m_tempSum(0.0),
      m_maxTemp(0),
      m_minTemp(200),
      m_numReadings(0)
{

}

void StatisticsDisplay::display()
{
    cout << "Avg/Max/Min temperature = " << m_tempSum / m_numReadings
         <<  "/" << m_maxTemp << "/" << m_minTemp << endl;
}

void StatisticsDisplay::update(double temp, double humidity, double pressure)
{
    (void) humidity;
    (void) pressure;

    m_tempSum += temp;
    m_numReadings++;

    if (temp > m_maxTemp)
    {
        m_maxTemp = temp;
    }

    if (temp < m_minTemp)
    {
        m_minTemp = temp;
    }

    display();
}

最後是測試程式碼:

int main(int argc, char *argv[])
{
    WeatherData weatherData;
    CurrentConditionDisplay currentDisplay;
    StatisticsDisplay staticDisplay;
    ForecastDisplay forcastDisplay;
    HeatIndexDisplay heatDisplay;
    weatherData.registerObserver(&currentDisplay);
    weatherData.registerObserver(&staticDisplay);
    weatherData.registerObserver(&heatDisplay);
    weatherData.registerObserver(&forcastDisplay);

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