1. 程式人生 > >觀察者模式詳解——參考《Head First設計模式》

觀察者模式詳解——參考《Head First設計模式》

觀察者模式的定義

定義了物件之間的一對多依賴,這樣一來,當一個物件狀態改變時,它的所有依賴者都會受到通知並自動更新。

觀察者模式的分析

本文是以氣象站系統為例說明觀察者模式的,簡單來說就是氣象收集到的資料組成了一個數據中心(也就是觀察者模式中的主題),不同的佈告板(觀察者)實時更新資料中心的資料給使用者,從而顯示不同的天氣預告。(如下分析中的類名請參考具體程式碼)

(1)從設計原則角度講,觀察者模式是具備鬆耦合條件的,鬆耦合是通過兩個基類Subject,和Observer實現的,有了Observer基類,WeatherData不需要知道各個佈告板的具體實現只需保證各個佈告板實現Observer介面,反之亦然。


(2)主題向觀察者“推送資訊” vs 觀察者向主題“請求資訊”。總體來說“請求資訊”的方式要優於“推送資訊”的方式,因為推送資訊是把觀察者感興趣與否的資訊都強制給觀察者,並且假設主題需要增加資訊,此時需要更改所有觀察者的實現(程式碼中的update函式),而“請求資訊”可以解決這兩個問題。

推送資訊方式:

	void update(float temperature, float humidity, float pressure) //push all the data to observers.
	{
		this->temperature = temperature;
		this->humidity = humidity;
		display();
	}

請求資訊方式:

	void update() //let observers pull data from subject.
	{
		this->temperature = weatherData->getTemperature();
		this->humidity = weatherData->getHumidity();
		display();
	}

(3)觀察者可以在執行期間訂閱主題或退訂主題,實現靈活的觀察模式。

訂閱主題:

	CurrentConditionDisplay(Subject* weatherData)
	{
		this->weatherData = weatherData;
		this->weatherData->registerObserver(this);
	}
退訂主題:
	void cancelSubscription()
	{
		this->weatherData->removeObserver(this);
	}
(4)可通過changed標誌位來實現觀察者更新資料的頻次的控制。
	void notifyObservers()
	{
		if(changed)
		{
			for(vector<Observer*>::iterator iter = observers.begin(); iter != observers.end(); ++iter)
			{
				//(*iter)->update(temperature, humidity, pressure); //push all the data to observers.
				(*iter)->update();
			}
		}
		changed = false;
	}

觀察者模式的程式碼

#include <iostream>
#include <vector>
#include <algorithm>
#include <crtdbg.h>

#define CRTDBG_MAP_ALLOC 

using namespace std;

class Observer
{
public:
	virtual void update(float temperature, float humidity, float pressure) {}; //push all the data to observers.
	virtual void update(){}; //let observers pull data from subject.
	virtual void display(){};
};

class Subject
{
public:
	virtual void registerObserver(Observer* o) = 0;
	virtual void removeObserver(Observer* o) = 0;
	virtual void notifyObservers() = 0;
	virtual float getTemperature(){return 0;};
	virtual float getHumidity(){return 0;};
	virtual float getPressure(){return 0;};
};

class WeatherData : public Subject
{
private:
	vector<Observer*> observers;
	float temperature;
	float humidity;
	float pressure;
	char changed;

public:
	WeatherData() : temperature(0), humidity(0), pressure(0), changed(false){}
	void registerObserver(Observer* o)
	{
		observers.push_back(o);
	}
	void removeObserver(Observer* o)
	{
		vector<Observer*>::iterator iter;
		iter = find(observers.begin(),observers.end(),o);
		if(iter != observers.end())
		{
			observers.erase(iter);
		}
		else
		{
			cout<<"The observer has not been registed."<<endl;
		}
	}
	void setChanged()
	{
		changed = true;
	}
	float getTemperature()
	{
		return temperature;
	}
	float getHumidity()
	{
		return humidity;
	}
	float getPressure()
	{
		return pressure;
	}
	void notifyObservers()
	{
		if(changed)
		{
			for(vector<Observer*>::iterator iter = observers.begin(); iter != observers.end(); ++iter)
			{
				//(*iter)->update(temperature, humidity, pressure); //push all the data to observers.
				(*iter)->update();
			}
		}
		changed = false;
	}
	void setMeasurements(float temperature, float humidity, float pressure)
	{
		this->temperature = temperature;
		this->humidity = humidity;
		this->pressure = pressure;
		measurementsChanged();
	}
	void measurementsChanged()
	{
		setChanged();
		notifyObservers();
	}
};

class CurrentConditionDisplay : public Observer
{
private:
	Subject *weatherData;
	float temperature;
	float humidity;
public:
	CurrentConditionDisplay(Subject* weatherData)
	{
		this->weatherData = weatherData;
		this->weatherData->registerObserver(this);
	}
	void cancelSubscription()
	{
		this->weatherData->removeObserver(this);
	}
	void update(float temperature, float humidity, float pressure) //push all the data to observers.
	{
		this->temperature = temperature;
		this->humidity = humidity;
		display();
	}
	void update() //let observers pull data from subject.
	{
		this->temperature = weatherData->getTemperature();
		this->humidity = weatherData->getHumidity();
		display();
	}
	void display()
	{
		cout<<"Current conditions: "<<temperature<<"F degrees and "<<humidity<<"% humidity"<<endl;
	}
};

class StatisticsDisplay : public Observer
{
private:
	Subject *weatherData;
	float temperature;
	float humidity;
	float pressure;
public:
	StatisticsDisplay(Subject *weatherData)
	{
		this->weatherData = weatherData;
		this->weatherData->registerObserver(this);
	}
	void cancelSubscription()
	{
		this->weatherData->removeObserver(this);
	}
	void update(float temperature, float humidity, float pressure) //push all the data to observers.
	{
		this->temperature = temperature;
		this->humidity = humidity;
		this->pressure = pressure;
		display();
	}
	void update() //let observers pull data from subject.
	{
		this->temperature = weatherData->getTemperature();
		this->humidity = weatherData->getHumidity();
		this->pressure = weatherData->getPressure();
		display();
	}
	void display()
	{
		cout<<"Statistics conditions: "<<temperature/10<<"F degree and "<<humidity/10<<"% humidity "<<pressure/10<<"% pressure "<<endl;
	}
};

int main()
{
	WeatherData *weatherData = new WeatherData();
	CurrentConditionDisplay currentDisplay(weatherData);
	StatisticsDisplay statisticsDisplay(weatherData);
	weatherData->setMeasurements(80,65,30.4);
	currentDisplay.cancelSubscription();
	weatherData->setMeasurements(70,64,20.3);
	delete weatherData;
	_CrtDumpMemoryLeaks(); 
	return 0;
}