zoukankan      html  css  js  c++  java
  • 观察者模式

                观察者模式(Observer):定义了对象之间的一对多关系,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。

                实现观察者模式的方法有多种,但是以包含Subject与Observer接口的类设计的做法最常见,下面看看观察者模式的类图:

                             

               主题(Subject)是真正拥有数据的人,观察者则是主题的依赖者,在主题数据变化时接收通知并更新。这样比起让许多对象控制同一份数据来,可以得到更干净的OO设计。

               主题与观察者之间是松耦合的,它们可以交互,但不知道彼此的细节。比如对于观察者,主题只知道观察者实现了某个接口,主题不需要知道观察者的具体类是谁、做了些什么或其他任何细节。任何时候我们都可以增加新的观察者,因为主题唯一依赖的东西是一个实现Observer接口的对象列表;同样的,我们也可以在任何时候删除某些观察者。因为松耦合的关系,改变主题或观察者其中一方,并不会影响另一方。松耦合的设计能让我们建立有弹性的OO系统以应对变化,因为对象之间的互相依赖降到了最低。

              下面就以经典的气象监测问题为例,说说观察者模式的应用:

               

          此气象站系统只包括三个部分:气象站、WeatherData对象、布告板。

          工作的流程:WeatherData对象从气象站获取最新的测量数据(温度、湿度、气压),并及时更新到三个布告板(显示装置)上。

          假设从气象站获取数据的方法已经实现好了,那么我们只需要考虑如何将新的数据更新到三个布告板上,而且要尽量实现系统可拓展,让其他开发人员可以定制布告板,用户可以任意的添加或删除布告板,那么我们如何建立这个系统呢?

          当然是使用观察者模式,这里的WeatherData类正是观察者模式中的“一”,即主题;而布告板就是“多”,即观察者;这样就建立起了一对多的依赖关系。WeatherData对象是真正拥有数据的一方,包括温度、湿度、气压,当这些值改变时,必须通知所有的布告板,好让它们各自做出处理。在这里,布告板作为Observer为了获取数据,必须先向WeatherData对象注册,一旦WeatherData知道有某个布告板的存在,就会适时地调用布告板的某个公共的接口(例如. update)来告诉布告板观测值是多少。由于update()方法是所有布告板公共的接口,所以需要在布告板的基类(Java中说接口)中定义。下面是设计图:


    • WeatherData实现主题(Subject)接口
    • 布告板实现观察者(Observer)接口,这样主题在需要通知观察者时,就有了一个共同的接口
    • 同时还为布告板建立一个共同的接口DisplayElement,用于实现display()方法
    • 每个布告板中应该声明一个Subject接口类对象

    C++实现:

    // Subject接口类
    #pragma once
    #include "Observer.h"
    
    class Subject   
    {
    public:
    	virtual void registerObserver(Observer* o) = 0;
    	virtual void removeObserver(Observer* o) = 0;
    	virtual void notifyObserver() = 0;
    };

    // Observer接口类
    #pragma once
    class Observer  
    {
    public:
    	virtual void update(float temp, float humidity, float pressure) = 0;
    };

    // 接口类 用于display
    #pragma once
    class DisplayElement
    {
    public:
    	virtual void display() = 0;
    };
    

    // WeatherData实现Subject接口
    #pragma once
    #include "Observer.h"
    #include "Subject.h"
    #include <list>
    
    class WeatherData : public Subject
    {
    private:
    	mutable std::list<Observer*> observers;
    	float temperature;
    	float humidity;
    	float pressure;
    
    public:
    	WeatherData(void);
    	~WeatherData(void);
    	void registerObserver(Observer* o);
    	void removeObserver(Observer* o);
    	void notifyObserver();
    	void measurementsChanged();
    	void setMeasurements(float temperature, float humidity, float pressure);
    	// 以下方法用于从气象台获取数据,这里不予考虑
        float getTemperature();
    	float getHumidity();
    	float getPressure();
    };
    
    
    // WeatherData.cpp
    #include "WeatherData.h"
    using namespace std;
    
    void WeatherData::registerObserver( Observer* o )
    {
    	observers.push_back(o);
    }
    
    void WeatherData::removeObserver( Observer* o )
    {
    	observers.remove(o);
    }
    
    void WeatherData::notifyObserver()
    {
    	for (list<Observer*>::iterator iterator=observers.begin(); observers.end()!=iterator; ++iterator)
    	{
    		Observer* observer = *iterator;
    		observer->update(temperature, humidity, pressure);
    	}
    }
    
    void WeatherData::measurementsChanged()
    {
    	notifyObserver();
    }
    
    void WeatherData::setMeasurements( float temperature, float humidity, float pressure )
    {
    	this->temperature = temperature;
    	this->humidity = humidity;
    	this->pressure = pressure;
    	measurementsChanged();
    }
    
    float WeatherData::getTemperature()
    {
    	return temperature;
    }
    
    float WeatherData::getHumidity()
    {
    	return humidity;
    }
    
    float WeatherData::getPressure()
    {
    	return pressure;
    }

    // “目前状况”布告板 实现观察者接口
    #pragma once
    #include "WeatherData.h"
    #include "Observer.h"
    #include "DisplayElement.h"
    
    class CurrentConditionsDisplay : public Observer, public DisplayElement
    {
    private:
    	float temperature;
    	float humidity;
    	Subject* weatherData;
    
    public:
    	CurrentConditionsDisplay(Subject* weatherData);
    	~CurrentConditionsDisplay(void);
    	void update(float temp, float humidity, float pressure);
    	void display();
    };
    
    // CurrentConditionsDisplay.cpp
    #include "CurrentConditionsDisplay.h"
    #include <iostream>
    using namespace std;
    
    
    CurrentConditionsDisplay::CurrentConditionsDisplay( Subject* weatherData )
    {
    	this->weatherData = weatherData;    // 构造器中注册为观察者
    	weatherData->registerObserver(this);
    }
    
    CurrentConditionsDisplay::~CurrentConditionsDisplay(void)
    {
    	weatherData->removeObserver(this);
    }
    
    void CurrentConditionsDisplay::update( float temp, float humidity, float pressure )
    {
    	this->temperature = temp;
    	this->humidity = humidity;
    	display();
    }
    
    void CurrentConditionsDisplay::display()
    {
    	cout.setf( ios::showpoint );
    	cout.precision(3);
    	cout << "Current conditions: "	<< temperature;
    	cout << " F degrees and " << humidity;
    	cout << "% humidity" << endl;
    }

    // 数据统计布告板
    #pragma once
    #include "WeatherData.h"
    #include "Observer.h"
    #include "DisplayElement.h"
    
    class StatisticsDisplay : public Observer, public DisplayElement
    {
    private:
    	Subject* weatherData;
        float maxTemp;
    	float minTemp;
    	float tempSum;
    	int numReadings;
    
    public:
    	StatisticsDisplay(Subject* weatherData);
    	~StatisticsDisplay(void);
    	void update(float temp, float humidity, float pressure);
    	void display();
    };
    
    
    // StatisticsDisplay.cpp
    #include "StatisticsDisplay.h"
    #include <iostream>
    using namespace std;
    
    StatisticsDisplay::StatisticsDisplay(Subject* weatherData)
    {
    	maxTemp = 0.0;      // 记录最高温
    	minTemp = 200.0F;   // 记录最低温
    	tempSum = 0.0;      // 温度和,用于计算平均温度
    	numReadings = 0;    // 次数,用于计算平均温度
    	this->weatherData = weatherData;    // 构造器中注册为观察者
    	weatherData->registerObserver(this);
    }
    
    
    StatisticsDisplay::~StatisticsDisplay(void)
    {
    	weatherData->removeObserver(this);
    }
    
    void StatisticsDisplay::update( float temp, float humidity, float pressure )
    {
    	tempSum += temp;
    	numReadings++;
    	if( temp > maxTemp ) {
    		maxTemp = temp;
    	}
    
    	if( temp < minTemp ) {
    		minTemp = temp;
    	}
    	display();
    }
    
    void StatisticsDisplay::display()
    {
    	cout.setf( ios::showpoint );
    	cout.precision(3);
    	cout << "Avg/Max/Min temperature = " << ( tempSum / numReadings );
    	cout << "/" << maxTemp << "/" << minTemp << endl;
    }
    

    // 天气预报布告板
    #pragma once
    #include "WeatherData.h"
    #include "Observer.h"
    #include "DisplayElement.h"
    
    class ForecastDisplay : public Observer, public DisplayElement
    {
    private:
    	float currentPressure;
    	float lastPressure;
    	Subject* weatherData;
    
    public:
    	ForecastDisplay(Subject* weatherData);
    	~ForecastDisplay(void);
    	void update(float temp, float humidity, float pressure);
    	void display();
    };
    
    
    // ForecastDisplay.cpp
    #include "ForecastDisplay.h"
    #include <iostream>
    using namespace std;
    
    ForecastDisplay::ForecastDisplay(Subject* weatherData)
    {
    	currentPressure = 29.92F;  // 假设现在的
    	lastPressure = 0.0;
    	this->weatherData = weatherData;    // 构造器中注册为观察者
    	weatherData->registerObserver(this);
    }
    
    
    ForecastDisplay::~ForecastDisplay(void)
    {
    	weatherData->removeObserver(this);
    }
    
    void ForecastDisplay::update( float temp, float humidity, float pressure )
    {
    	lastPressure = currentPressure;
    	currentPressure = pressure;
    	display();
    }
    
    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;
    
    }
    

    // 程序入口WeatherStation.cpp
    #include "WeatherData.h"
    #include "CurrentConditionsDisplay.h"
    #include "ForecastDisplay.h"
    #include "StatisticsDisplay.h"
    
    int main(){
    	WeatherData* weatherData = new WeatherData;
    
    	CurrentConditionsDisplay* cu = new CurrentConditionsDisplay(weatherData);
    	StatisticsDisplay* st = new StatisticsDisplay(weatherData);
    	ForecastDisplay* fo = new ForecastDisplay(weatherData);
        // 更新数据,这里手动模拟
    	weatherData->setMeasurements( 80, 65, 30.4f );
    	weatherData->setMeasurements( 82, 70, 29.2f );
    	weatherData->setMeasurements( 78, 90, 29.2f );
    
    	getchar();
    	return 0;
    }

    运行结果:





    个人站点:http://songlee24.github.io/

  • 相关阅读:
    多线程创建方式四种

    归并排序
    Spark调优之--资源调优、并行度调优
    多线程中的上下文切换
    守护线程和本地线程
    线程和进程的区别
    3. 无重复字符的最长子串
    [蓝桥杯][历届试题]连号区间数
    [蓝桥杯][历届试题]蚂蚁感冒
  • 原文地址:https://www.cnblogs.com/songlee/p/5738165.html
Copyright © 2011-2022 走看看