Head First Design Patterns 阅读笔记之二: Observer Pattern
2016-08-30 15:37
519 查看
从一款天气预报 APP 开始
假设现在需要编写一款 APP,可以显示感应器传来的当前天气条件(温度、湿度、气压),报告当前的天气状况统计数据并做简单的预测。现在已知以下条件:WeatherData 类有三个 getter 方法来获取当前的数据:getTemperature()、getHumidity()、getPressure()。
WeatherData 类中有一个方法 measurementsChanged() ,每当新的天气数据获得的时候就被调用。
我们需要利用当前的数据显示上面提到的三个方面,并且在数据更新时需要更新显示内容。
系统必须易扩展,其他开发者可以添加新的显示项。
初稿完成?
根据要求写下如下代码:public class WeatherData { // instance variable declarations 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); } // other WeatherData methods here }
这样看上去对,但是存在很多问题:
- 由于 currentConditionDisplay 几个类都是具体实现的,所以动态添加其他显示选项,完全不可能。
- temp、humidity、pressure 这些会变化的地方最好使用接口,由设计原则可知,我们需要封装会变化的地方。
观察者模式
首先看一下杂志订阅的过程:杂志出版商开始出版杂志。
你订阅一家杂志,每当它退出新刊物,就会送给你。只要你保持订阅,就始终会获得新的杂志。
一旦你停止订阅,就不会再给你送杂志。
只要杂志出版商还存在,就会有人订阅或取消订阅。
观察者模式可以用如下公式定义:
Publishers + Subscribers = Observer Pattern
在观察者模式中,我们把出版商叫做 Subject,订阅者叫做Observer。正式定义如下:
The Observer Pattern defines a one-to-many dependency between objects so that when one objet changes state, all of its dependents are notified and updated automatically.
在后面,你会发现有不同的方式实现观察者模式,但是大多数都以设计包含 Subject 和 Observer 接口的类为核心。一般的设计如下图:
松耦合的好处
如果两个对象是松耦合的,它们互相交流但是彼此了解很少。观察者模式使得 subject 和 observer 是松耦合的。为什么?subject 只知道 observer 实现了某一个接口,所以我们可以在任意时刻增加或删除新的 observer。
增加新的 observer 类型时,我们不需要修改 subject。
我们可以独立的重用 subject 和 observer,它们当中一个的改变并不会影响另一个。
这里需要提到一个新的设计原则
在有联系的对象之间追求松耦合。
Strive for loosely coupled designs between objects that interact.
修改后的版本
画出相应的设计图:// 几个接口 public interface Subject { public void registerObserver(Observer o); public void removeObserver(Observer o); public void notifyObserver(); } public interface Observer { public void update(float temp, float humidity, float pressure); } public interface DisplayElement { public void display(); }
// 实现 Subject import java.util.ArrayList; public class WeatherData implements Subject { private ArrayList 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 (int i = 0; i < observers.size(); i++) { Observer observer = (Observer)observers.get(i); observer.update(temperature, humidity, pressure); } } public void measurementsChanged() { notifyObservers(); } public void setMeasurements(float temperature, float humidity, float pressure) { this.temperature = temperature; this.humidity = humidity; this.pressure = pressure; measurementsChanged(); } // other WeatherData methods here }
// 实现 Observer public class CurrentConditionsDisplay implements Observer, DisplayElement { private float temperature; private float humidity; private Subject weatherData; public CurrentConditionsDisplay(Subject weatherData) { this.weatherData = weatherData; weatherData.registerObserver(this); } public void update(float temperature, float humidity, float pressure) { this.temperature = temperature; this.humidity = humidity; display(); } public void display() { System.out.println("Current conditions: " + temperature + "F degrees and " + humidity + "% humidity"); } }
// 测试程序 public class WeatherStation { public static void main(String[] args) { WeatherData weatherData = new WeatherData(); CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData); weatherData.setMeasurements(80, 65, 30.4f); weatherData.setMeasurements(82, 70, 29.2f); weatherData.setMeasurements(78, 90, 29.2f); } }
一般信息都是 observer 用 pull 的方式获取的,当然也可以使用 push 方式(内置的观察者模式可以实现),但是我们一般认为 pull 方式更好。
利用 Java 内置的观察者模式
Java API 中拥有内置的观察者模式:java.util 包中的 Observable 类和 Observer 接口。它们分别对应于前面的 Subject 和 Observer 接口。使用情况如下:现在利用内置的 API 重写程序:
但是直接使用内置的 API 存在问题:
observable 是一个类,而不是接口
observable 中一些关键方法的访问修饰符是 protected。所有必须是子类才能调用。
相关文章推荐
- PropertyChangeListener简单理解
- 什么是设计模式
- 设计模式之创建型模式 - 特别的变量问题
- 七、设计模式——装饰模式
- 设计模式总结
- 设计模式之创建型模式
- 浅谈设计模式的学习
- 设计模式---状态模式在web前端中的应用
- Ruby设计模式编程之适配器模式实战攻略
- 实例讲解Ruby使用设计模式中的装饰器模式的方法
- 设计模式中的模板方法模式在Ruby中的应用实例两则
- Ruby设计模式编程中对外观模式的应用实例分析
- 实例解析Ruby设计模式编程中Strategy策略模式的使用
- Ruby中使用设计模式中的简单工厂模式和工厂方法模式
- Ruby使用设计模式中的代理模式与装饰模式的代码实例
- 详解组合模式的结构及其在Ruby设计模式编程中的运用
- C# 设计模式系列教程-建造者模式
- C#编程中使用设计模式中的原型模式的实例讲解
- 使用设计模式中的工厂方法模式进行C#编程的示例讲解
- 实例解析C#设计模式编程中简单工厂模式的使用