HeadFirst 设计模式学习笔记2--观察者模式

1.我们需要理解报社、订阅系统和订报人之间的关系,订报人通过订阅系统订报,一旦报社有新的报纸,订阅系统就会派人送或者邮寄给订报人新的报纸。然后,出版者+订阅者就是观察者模式,只不过名称不一样,主题(Subject,或者叫做Observabler)类比于出版者,订阅者改称为观察者(Observer)。该模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新,这样的模式让主题和观察者之间松耦合。它体现了第四个原则:为了交互对象之间的松耦合设计而努力。

2.要建立一个气象站--气象搜集装置--多个类型的气象发布版的一个系统:

我们首先定义两个接口,实现这两个接口的类就是相应的主题类--对应于气象搜集装置,或者是观察者类--对应气象发布版:

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(); }

我们先根据主题接口实现气象搜集装置:

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 public float getTemperature() { return temperature; } public float getHumidity() { return humidity; } public float getPressure() { return pressure; } }

我们再根据观察者接口实现多种气象发布版,这里只举出一个例子,其余大同小异:

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); StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData); ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData); weatherData.setMeasurements(80, 65, 30.4f); weatherData.setMeasurements(82, 70, 29.2f); weatherData.setMeasurements(78, 90, 29.2f); } } 

请注意,这里主机主要是采用“推”的方式进行数据传送的,传送的数据在不同的发布版中并没有都使用。

3.Java内置的观察者模式

我们使用Java内置的观察者模式再实现一遍气象站的项目(省略导入适当的包的过程):

public class WeatherData extends Observable {//直接继承了Java提供的主题超类,注意这里Observable是一个类 private float temperature; private float humidity; private float pressure; public WeatherData() { }//由于采用内置的主题,这里就不需要你手动维护一个观察者列表了。 public void measurementsChanged() { setChanged();//提示数据已经更新,是内置方法 notifyObservers();//没有采用带有参数的调用,说明是采用观察者需要时主动“拉”数据的方式。 } public void setMeasurements(float temperature, float humidity, float pressure) { this.temperature = temperature; this.humidity = humidity; this.pressure = pressure; measurementsChanged(); } public float getTemperature() {//下边这三个方法就是为了配合“拉”数据模式而提供的方法 return temperature; } public float getHumidity() { return humidity; } public float getPressure() { return pressure; } }

我们现在再创建一个使用Java内置观察者模式完成的气象发布版: 

public class CurrentConditionsDisplay implements Observer , DisplayElement {//注意这里Observer是一个接口 Observable observable;//主题的引用,用于注册、注销等操作 private float temperature; private float humidity; public CurrentConditionsDisplay(Observable observable) { this.observable = observable; observable.addObserver(this);//注册 } public void update(Observable obs, Object arg) { if (obs instanceof WeatherData) {//判断是不是属于WeatherData类 WeatherData weatherData = (WeatherData)obs; this.temperature = weatherData.getTemperature();//这里体现了拉的方式 this.humidity = weatherData.getHumidity(); 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 currentConditions = new CurrentConditionsDisplay(weatherData); StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData); ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData); weatherData.setMeasurements(80, 65, 30.4f); weatherData.setMeasurements(82, 70, 29.2f); weatherData.setMeasurements(78, 90, 29.2f); } } 

内置的观察者模式有一定的明显缺陷,首先它是一个类,这个就与我们的第一个原则不符合,若一个类想以观察者模式完成要从另一个继承来的功能就会陷入两难。而且它将关键方法setChanged()设置为protected,这就意味着你必须继承Observable,否则无法创建Observable实例并组合到你设计的对象中。 

 

示例代码下载

你可能感兴趣的:(HeadFirst 设计模式学习笔记2--观察者模式)