观察者模式学习笔记

什么时候需要用观察者模式?


有的时候我们有这样的需求,当某一对象的状态更新了,需要告知其他的对象。同时,其他对象可以退出通知列表,也可以选择重新加入通知列表。这时候,我们就需要用到一种设计模式——观察者模式。

场景举例


上面的话太难以理解了,模拟一个应用场景来进行说明。
有一个气象站,能通过一些硬件设备获取到一些气象数据(温度、湿度、大气压)。同时有几个显示屏幕——实时信息、数据统计、气象预报等屏幕。当气象数据更新了之后,这些屏幕也要随着更新数据。另外,该应用还要扩展友好,能提供API接口,能添加或者删除屏幕设备。

场景分析

先分析一下场景,能发现几个对象(object)。气象数据(WeatherData),实时信息屏幕(CurrentConditionDisplay),数据统计屏幕(statisticsDisplay),气象预报屏幕(ForcastDisplay)。

/**
 * 气象数据
 */
class WeatherData {
	getTemperature();
    getHumidity();
    getPressure();
    measurementsChanged();
}
/**
 * 实时信息屏幕
 */
class CurrentConditionDisplay() {
	display();
}
/**
 * 数据统计屏幕
 */
class statisticsDisplay() {
	display();
}
/**
 * 气象预报屏幕
 */
class ForcastDisplay() {
	display();
}

先完善一下 WeatherData的代码

public class WeatherData {
    public void measurementsChanged() {
        float temp = getTemperature();
        float humidity = getHumidity();
        float pressure = getPressure();

        currentConditionsDisplay.update(temp, humidity, pressure);
        statisticsDisplay.update(temp, humidity, pressure);
        forecastDisplay.update(temp, humidity, pressure);
    }    
}

发现不足

上面WeatherData的这段代码好像完成了数据更新的功能。但是有几个不足的地方。

  • 针对实现编程,而不是针对接口编程
  • 当有新的显示屏幕,我们需要改动WeatherData 中的measurementsChanged()方法
  • 不能在程序运行是时添加或移除显示屏对象
  • 所有的显示屏对象虽然都有同一个update()方法,却没有实现一个通用接口

如何改进?

  1. 使用接口,这样就能在程序运行时增加或移除显示屏幕
  2. 所有的显示屏幕实现一个通用显示接口,实现该接口的update()方法。

改进的具体实现,先放着。先回到观察者模式上来。

重新审视观察者模式

举个更接近观察者模式定义的例子:

就像订阅报纸一样,你可以订阅报纸,这样当有新的报纸到了,就有新的报纸看。如果你取消订阅了,那下一期的报纸就无法看到了。

上面这句话有两种行为发布订阅。观察者模式其实就是这样的

Publisher + Observer = Observer Pattern

翻译成中文就是:发布者 + 观察者 = 观察者模式

说了这么多,是时候来一个官方定义了。

The Observer Pattern defines a one-to-many dependency between objects so that when one object changes state, all of its dependents are notified and updated automatically.

翻译一下就是:

观察者模式 定义了对象间一对多的关系。所以当一个对象的状态变化了,它的依赖对象就会被唤醒和自动更新状态。

注意关键点:

  • 一对多
  • 状态变化时,依赖对象随之更新

如何实现这个程序?

解耦

设计原则:Strive for loosely coupled designs between objects that interact.

翻译过来就是:为它们之间有交互的对象解耦而努力

代码关系如下:

/**
 * Publisher接口
 */
interface Subject {
    registerObserver();
    removeObserver();
    notifyObserver();
}
/**
 * 气象数据实现Publisher接口,作为一个Subject
 */
public class WeatherData implements Subject {

	private float temp;
	private float humidity;
	private float pressure;

	private List<IObserver> observers;

	public WeatherData() {
		observers = new ArrayList<IObserver>();
	}

	@Override
	public void registerObserver(IObserver observer) {
		observers.add(observer);
	}

	@Override
	public void removeObserver(IObserver observer) {
		int index = observers.indexOf(observer);

		if (index > 0) {
			observers.remove(index);
		}
	}

	@Override
	public void notifyObserver() {
		for (IObserver ob : observers) {
			ob.update(getTemp(), getHumidity(), getPressure());
		}
	}

	public void measurementsChanged() {
		notifyObserver();
	}

	public void setMeasurements(float temp, float humidity, float pressure) {
		this.temp = temp;
		this.humidity = humidity;
		this.pressure = pressure;

		measurementsChanged();
	}

	public float getTemp() {
		return temp;
	}

	public void setTemp(float temp) {
		this.temp = temp;
	}

	public float getHumidity() {
		return humidity;
	}

	public void setHumidity(float humidity) {
		this.humidity = humidity;
	}

	public float getPressure() {
		return pressure;
	}

	public void setPressure(float pressure) {
		this.pressure = pressure;
	}

}

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

因为所有的显示屏都有一个update()方法,因此规定一个公共接口

/**
 * 屏幕显示接口
 */
public interface IDisplayElement {	
	public void display();
}

接着实现Observer即显示屏幕

/**
 * 实时信息显示屏
 */
public class ConditionsDisplay implements Observer, DisplayElement {

	private float temp;
	private float humidity;
	private ISubject weatherData;

	public ConditionsDisplay(ISubject weatherData) {
		this.weatherData = weatherData;
		this.weatherData.registerObserver(this);
	}

	@Override
	public void update(float temp, float humidity, float pressure) {
		this.temp = temp;
		this.humidity = humidity;

		display();
	}

	@Override
	public void display() {
		System.out.println("Current conditions: " + temp + "F degrees and "
				+ humidity + "% humidity");
	}
}

其他的几个显示屏类也是类似的,就不写了。

接着写一个测试类App来进行验证

public class App {

	public static void main(String[] args) {
		WeatherData weatherData = new WeatherData();
		ConditionsDisplay conditionsDisplay = new ConditionsDisplay(weatherData);
		
		weatherData.setMeasurements(10.3f, 45.12456f, 123.456f);
	}
	
}

打印结果:

output:
-----------------------------------------------------------
Current conditions: 10.3F degrees and 45.12456% humidity

总结

  1. 观察者模式是 one-to-many 的关系。一个对象变化,其依赖的对象也随之更新
  2. 为了扩展性,应该是面向接口编程而不是面向实现编程
  3. Publisher + Observer = Observer Pattern
posted @ 2015-04-21 08:47  红尘炼心  阅读(283)  评论(0编辑  收藏  举报