C++实现观察者模式Observer Pattern

设计原则4:为了交互对象之间的松耦合设计而努力。

松耦合的设计之所以能让我们建立有弹性的OO系统,能够应对变化,是因为对象之间的互相依赖降到了最低。


观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的多有依赖者都会收到通知并自动更新。


定义接口

#include <iostream>
#include <list>
using namespace std;

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

class Subject {
public:
	virtual void registerObserver(Observer* o) = 0;
	virtual void removeObserver(Observer* o) = 0;
	virtual void notifyObserver() = 0;
};

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

定义被观察者(主题)

使用std::list来代替Java中的ArrayList。list有unique函数和remove函数,使用起来比vector更方便。

class WeatherData :public Subject {
private:
	list<Observer*> observers;
	float temperature;
	float humidity;
	float pressure;
public:
	void registerObserver(Observer *o) {
		observers.push_back(o);
		observers.unique();
	}

	void removeObserver(Observer *o) {
		observers.remove(o);
	}

	void notifyObserver() {
		for (list<Observer*>::iterator itor = observers.begin(); itor != observers.end(); ++itor) {
			(*itor)->update(temperature, humidity, pressure);
		}
	}

	void measurementsChanged() {
		notifyObserver();
	}

	void setMeasurements(float temp, float humi, float pres) {
		temperature = temp;
		humidity = humi;
		pressure = pres;
		measurementsChanged();
	}
};

定义观察者,书中定义了4个观察者

class CurrentConditionsDisplay : public Observer, public DisplayElement {
private:
	float m_temperature = 0;
	float m_humidity = 0;
	Subject* m_weatherData;
public:
	CurrentConditionsDisplay(Subject *weatherData) {
		m_weatherData = weatherData;
		m_weatherData->registerObserver(this);
	}

	~CurrentConditionsDisplay() {
		m_weatherData->removeObserver(this);
	}

	void update(float temperature, float humidity, float pressure) {
		m_temperature = temperature;
		m_humidity = humidity;
		display();
	}

	void display() {
		printf("Current conditions: %fF degrees and %f%% humidity.\n", m_temperature, m_humidity);
	}
};

class StatisticsDisplay : public Observer, public DisplayElement {
private:
	float maxTemp = 0.0f;
	float minTemp = 200;
	float tempSum = 0.0f;
	int numReadings = 0;
	WeatherData* m_weatherData;

public:
	StatisticsDisplay(WeatherData* weatherData) {
		m_weatherData = weatherData;
		m_weatherData->registerObserver(this);
	}

	~StatisticsDisplay() {
		m_weatherData->removeObserver(this);
	}

	void update(float temp, float humidity, float pressure) {
		tempSum += temp;
		numReadings++;

		if (temp > maxTemp) {
			maxTemp = temp;
		}
		if (temp < minTemp) {
			minTemp = temp;
		}

		display();
	}

	void display() {
		printf("Avg/Max/Min temperature = %f/%f/%f.\n", (tempSum / numReadings), maxTemp, minTemp);
	}
};

class ForecastDisplay : public Observer, public DisplayElement {
private:
	float currentPressure = 29.92f;
	float lastPressure = 29.92f;
	WeatherData* m_weatherData;

public:
	ForecastDisplay(WeatherData* weatherData) {
		m_weatherData = weatherData;
		m_weatherData->registerObserver(this);
	}

	~ForecastDisplay() {
		m_weatherData->removeObserver(this);
	}

	void update(float temp, float humidity, float pressure) {
		lastPressure = currentPressure;
		currentPressure = pressure;
		display();
	}

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

class HeatIndexDisplay : public Observer, public DisplayElement {
private:
	float heatIndex = 0.0f;
	WeatherData* m_weatherData;

	double 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;
	}
public:
	HeatIndexDisplay(WeatherData* weatherData) {
		m_weatherData = weatherData;
		m_weatherData->registerObserver(this);
	}

	~HeatIndexDisplay() {
		m_weatherData->removeObserver(this);
	}

	void update(float temp, float humidity, float pressure) {
		heatIndex = computeHeatIndex(temp, humidity);
		display();
	}

	void display() {
		printf("Heat Index = %f\n", heatIndex);
	}
};

测试

void main() {
	WeatherData *weatherData = new WeatherData();
	CurrentConditionsDisplay* currentDisplay = new CurrentConditionsDisplay(weatherData);
	StatisticsDisplay* statisticsDisplay = new StatisticsDisplay(weatherData);
	ForecastDisplay* forecastDisplay = new ForecastDisplay(weatherData);
	HeatIndexDisplay* heatIndexDisplay = new HeatIndexDisplay(weatherData);

	weatherData->setMeasurements(80, 65, 30.4f);
	delete statisticsDisplay;
	weatherData->setMeasurements(82, 70, 29.2f);
	delete forecastDisplay;
	weatherData->setMeasurements(78, 90, 29.2f);
	delete heatIndexDisplay;
	weatherData->setMeasurements(76, 82, 29.8f);
}

观察者模式的优点:

1. 可以让表示层和数据逻辑层分离,并在主题和观察者之间建立一个抽象的耦合 (低耦合,主题只知道观察者实现了 Observer 接口)
2. 建立了一套触发机制,支持广播式通信

观察者模式的缺点:

1. 如果一个主题对象有很多观察者的话,将所有的观察者都通知到会花费很多时间
2. 如果观察者和主题对象有循环依赖的话,主题对象会触发它们之间进行循环调用,可能导致系统崩溃
3. 没有相应的机制让观察者知道主题对象是怎么发生变化的,而仅仅只是知道主题对象发生了变化
 


参考:

1. EricFreeman, FreemanElisabeth, 弗里曼, et al. Head First设计模式[M]. 中国电力出版社, 2007.

2. C++设计模式之观察者模式(行为型模式)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值