《Head First 设计模式》读书笔记2:观察者(Observer)模式

例子

需求:已知有一个WeatherData对象负责追踪目前的天气状况(温度、湿度、气压)。WeatherData中有以下方法:getTemperature(), getHumidity(), gePressure(), measurementsChanged()。前三个方法用于获取最新的温度、湿度、气压,第四个方法会在气象测量更新时被调用,但是具体实现需要自己完成。

  1. 需要建立一个应用,有三个布告板,分别显示目前的状况、气象统计以及天气预报。
  2. 当WeatherObject对象获取最新的测量数据时,三种布告板必须实时更新。
  3. 公布一组api,使得其他开发人员可以写出自己的气象公布板。

第一种实现:

public void measurementsChanged() {
	//获取最新的温度、湿度、气压
	float temp = getTemperature();
	float humidity = getHumidity();
	float pressure = gePressure();
	//更新所有布告板
	currentConditionDisplay.update(temp, humidity, pressure);
	statisticsDisplay.update(temp, humidity, pressure);
	forecastConditionDisplay.update(temp, humidity, pressure);
}

这种实现很容易理解,但是如果我们需要添加新的布告板了呢?是不是就必须修改代码。可以看到,它没有针对接口编程,布告板的更新都是针对具体实现,这会导致以后再增加或者删除布告板时必须修改程序。而其实每种布告板的方法更像是一个统一的接口,同样的方法名,同样的参数。
第二种实现:
现在我们来了解下观察者模式。举报纸订阅为例,报社每天发放报纸给订阅者,没有订阅该报纸的便不会收到报纸。用户可以随时订阅,也可以随时取消订阅。我们统一称类似于报社这样的主体为“主题”(Subject),订阅者为观察者(Observer)。
《Head First 设计模式》读书笔记2:观察者(Observer)模式_第1张图片
具体的主题实现主题的接口,具体的观察者实现观察者的接口。主题和观察者成“一对多”的关系。观察者通过主题的接口订阅或删除具体的主题,主题则调用观察者的接口里的update()。
通过这种方法,两个对象之间是松耦合的。主题只知道观察者实现了某个接口(也就是Observer接口),但是不知道观察者的具体类是什么。我们任何时候都可以增加新的观察者,而不会影响主题的代码。

//主题接口
public interface Subject {
	public void registerObserver(Observer o);
	public void removeObserver(Observer o);
	public void notifyObservers();
}

//观察者接口
public interface Observer {
	public void update(float temp, float humidity, float pressure);
}

//用于布告板显示数据的接口
public interface DisplayElement {
	public void display();
}
//具体的主题,此处即为WeatherData
public class WeatherData implements Subject {
	//维护订阅该主题的观察者列表,保存的是接口
	private List<Observer> observers;
	private float temperature;
	private float humidity;
	private float pressure;

	public WeatherData() {
		observers = new ArrayList();
	}

	//注册观察者
	public void registerObserver(Observer o) {
		observers.add(o);
	}
	
	//移除观察者
	public void removeObserver(Observer o) {
		int i = observers.indexof(o);
		if (i > 0) {
			observers.remove(i);
		}
	}
	
	//通知观察者
	public void notifyObservers() {
		for (Observer o : observers) {
			observer.update(temp,  humidity,  pressure);
		}
	}

	//当气象测量更新时调用
	public void measurementsChanged() {
		notifyObservers();
	}

	//气象测量更新
	public void setMeasurements(float temp, float humidity, float pressure) {
		this.temperature = temp;
		this.humidity = humidity;
		this.pressure = pressure;
		measurementsChanged();
	}
}
//具体的观察者,这里是目前状况公告板。其他公告板也类似。
public class CurrentConditionDisplay implements Observer, DisplayElement {
	private float temperature;
	private float humidity;
	private float pressure;
	//订阅的主题,类型是接口
	private Subject weatherData;

	//在构造器中注册主题
	public CurrentConditionDisplay(Subject weatherData) {
		this.weatherData = weatherData;
		weatherData.registerObserver(this);
	}

	//当气象更新时,WeatherData会调用观察者的此方法
	public void update(float temp, float humidity, float pressure) {
		this.temperature = temp;
		this.humidity = humidity;
		this.pressure = pressure;
		display();
	}

	//数据展示
	public void display() {
		//展示当前的天气状况
		...
	}
}

如此,我们的气象系统就构建完成了。再来看看一开始的需求:

  1. 三个公告板。我们这里只实现了一个,但是都是类似,只要修改下display即可。
  2. 实时更新。当气象系统更新时,会遍历所有订阅它(主题)的布告板(观察者),然后调用他们的update()方法。
  3. 增加新的布告板。当有新的布告板时,我们的实现是非常简单的。新的布告板类只需要实现Observer和DisplayElement两个接口,然后分别重写它们的update()和display()。然后在声明时,只要在构造器中传入WeatherData的对象即可。

总结

设计模式:观察者模式

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

设计原则

  1. 找到程序中会变化的方面,然后将其和固定不变的方面相分离。 在观察者模式中,会改变的时主题的状态,以及观察者的数目和类型。用这个模式,你可以改变依赖于主题状态的对象,却不必改变主题。这个就叫提前规划。
  2. 针对接口编程,不针对实现编程。 主题和观察者都是用接口,观察者利用主题的接口向主题注册,而主题利用观察者接口通知观察者。这样可以让两者之间运作正常,又同时具有松耦合的优点。
  3. 多用组合,少用继承。 观察者模式利用“组合”将许多观察者组合进主题中。对象之间的这种关系不是通过继承产生的,而是在运行时利用组合的方式而产生的。
  4. 为交互对象之间的松耦合设计而努力。 以上。

你可能感兴趣的:(设计模式)