观察者模式(一)--《Head First DesignPattern》

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

我们先看下类图:

首先我们自己创建Subject接口,定义了注册观察者,移除观察者和通知观察者三个函数。

1 package headfirst.observer.weather;
2 
3 public interface Subject {
4     public void registerObserver(Observer o);
5     public void removeObserver(Observer o);
6     public void notifyObservers();
7 }

而WeatherData实现了这个接口,内部维护的是一个ArrayList的Observer。

 1 package headfirst.observer.weather;
 2 
 3 import java.util.*;
 4 
 5 public class WeatherData implements Subject {
 6     private ArrayList observers;
 7     private float temperature;
 8     private float humidity;
 9     private float pressure;
10     
11     public WeatherData() {
12         observers = new ArrayList();
13     }
14     
15     public void registerObserver(Observer o) {
16         observers.add(o);
17     }
18     
19     public void removeObserver(Observer o) {
20         int i = observers.indexOf(o);
21         if (i >= 0) {
22             observers.remove(i);
23         }
24     }
25     
26     public void notifyObservers() {
27         for (int i = 0; i < observers.size(); i++) {
28             Observer observer = (Observer)observers.get(i);
29             observer.update(temperature, humidity, pressure);
30         }
31     }
32     
33     public void measurementsChanged() {
34         notifyObservers();
35     }
36     
37     public void setMeasurements(float temperature, float humidity, float pressure) {
38         this.temperature = temperature;
39         this.humidity = humidity;
40         this.pressure = pressure;
41         measurementsChanged();
42     }
43     
44     public float getTemperature() {
45         return temperature;
46     }
47     
48     public float getHumidity() {
49         return humidity;
50     }
51     
52     public float getPressure() {
53         return pressure;
54     }
55 }

DisplayElement接口只包含了一个方法,也就是display()。但布告板需要显示时,调用此方法。

1 package headfirst.observer.weather;
2 
3 public interface DisplayElement {
4     public void display();
5 }

Observer接口定义了update函数,当Subject的内容发生改变时,会调用update函数来通知观察者更新状态值。

1 package headfirst.observer.weather;
2 
3 public interface Observer {
4     public void update(float temp, float humidity, float pressure);
5 }

观察者需要存储Subject的引用,通过这个引用来进行注册。

 1 package headfirst.observer.weather;
 2     
 3 public class CurrentConditionsDisplay implements Observer, DisplayElement {
 4     private float temperature;
 5     private float humidity;
 6     private Subject weatherData;
 7     
 8     //构造函数的参数为Subject
 9     public CurrentConditionsDisplay(Subject weatherData) {
10         this.weatherData = weatherData;
11         //把自己注册给Subject
12         weatherData.registerObserver(this);
13     }
14     
15     //更新时调用相应的display函数
16     public void update(float temperature, float humidity, float pressure) {
17         this.temperature = temperature;
18         this.humidity = humidity;
19         display();
20     }
21     
22     public void display() {
23         System.out.println("Current conditions: " + temperature 
24             + "F degrees and " + humidity + "% humidity");
25     }
26 }
27         

其他两个差不多:

ForecastDisplay
 1 package headfirst.observer.weather;
 2 
 3 import java.util.*;
 4 
 5 public class ForecastDisplay implements Observer, DisplayElement {
 6     private float currentPressure = 29.92f;  
 7     private float lastPressure;
 8     private WeatherData weatherData;
 9 
10     public ForecastDisplay(WeatherData weatherData) {
11         this.weatherData = weatherData;
12         weatherData.registerObserver(this);
13     }
14 
15     public void update(float temp, float humidity, float pressure) {
16                 lastPressure = currentPressure;
17         currentPressure = pressure;
18 
19         display();
20     }
21 
22     public void display() {
23         System.out.print("Forecast: ");
24         if (currentPressure > lastPressure) {
25             System.out.println("Improving weather on the way!");
26         } else if (currentPressure == lastPressure) {
27             System.out.println("More of the same");
28         } else if (currentPressure < lastPressure) {
29             System.out.println("Watch out for cooler, rainy weather");
30         }
31     }
32 }

StatisticsDisplay

 1 package headfirst.observer.weather;
 2 
 3 import java.util.*;
 4 
 5 public class StatisticsDisplay implements Observer, DisplayElement {
 6     private float maxTemp = 0.0f;
 7     private float minTemp = 200;
 8     private float tempSum= 0.0f;
 9     private int numReadings;
10     private WeatherData weatherData;
11 
12     public StatisticsDisplay(WeatherData weatherData) {
13         this.weatherData = weatherData;
14         weatherData.registerObserver(this);
15     }
16 
17     public void update(float temp, float humidity, float pressure) {
18         tempSum += temp;
19         numReadings++;
20 
21         if (temp > maxTemp) {
22             maxTemp = temp;
23         }
24  
25         if (temp < minTemp) {
26             minTemp = temp;
27         }
28 
29         display();
30     }
31 
32     public void display() {
33         System.out.println("Avg/Max/Min temperature = " + (tempSum / numReadings)
34             + "/" + maxTemp + "/" + minTemp);
35     }
36 }

HeadDisplay

 1 package headfirst.observer.weather;
 2 
 3 public class HeatIndexDisplay implements Observer, DisplayElement {
 4     float heatIndex = 0.0f;
 5     private WeatherData weatherData;
 6 
 7     public HeatIndexDisplay(WeatherData weatherData) {
 8         this.weatherData = weatherData;
 9         weatherData.registerObserver(this);
10     }
11 
12     public void update(float t, float rh, float pressure) {
13         heatIndex = computeHeatIndex(t, rh);
14         display();
15     }
16     
17     private float computeHeatIndex(float t, float rh) {
18         float index = (float)((16.923 + (0.185212 * t) + (5.37941 * rh) - (0.100254 * t * rh) 
19             + (0.00941695 * (t * t)) + (0.00728898 * (rh * rh)) 
20             + (0.000345372 * (t * t * rh)) - (0.000814971 * (t * rh * rh)) +
21             (0.0000102102 * (t * t * rh * rh)) - (0.000038646 * (t * t * t)) + (0.0000291583 * 
22             (rh * rh * rh)) + (0.00000142721 * (t * t * t * rh)) + 
23             (0.000000197483 * (t * rh * rh * rh)) - (0.0000000218429 * (t * t * t * rh * rh)) +
24             0.000000000843296 * (t * t * rh * rh * rh)) -
25             (0.0000000000481975 * (t * t * t * rh * rh * rh)));
26         return index;
27     }
28 
29     public void display() {
30         System.out.println("Heat index is " + heatIndex);
31     }
32 }

接下来就是main函数了。

 1 package headfirst.observer.weather;
 2 
 3 import java.util.*;
 4 
 5 public class WeatherStationHeatIndex {
 6 
 7     public static void main(String[] args) {
 8         WeatherData weatherData = new WeatherData();
 9         CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData);
10         StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData);
11         ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData);
12         HeatIndexDisplay heatIndexDisplay = new HeatIndexDisplay(weatherData);
13 
14         weatherData.setMeasurements(80, 65, 30.4f);
15         weatherData.setMeasurements(82, 70, 29.2f);
16         weatherData.setMeasurements(78, 90, 29.2f);
17     }
18 }

可以看到这里实现的Observer模式有两个要注意的地方

  1. “推”模式,Subject内容一有变化,就主动向Observer推送消息。它是通过在Subject中的notifyObservers中对每个Observer调用update来实现的。如果要实现“拉”模式,因为在Observer保存了Subject的引用,所以可以通过定时的方式,向Subject拉取数据。
  2. 可以看到update函数是依赖于具体实现的,可以看到参数是什么温度,湿度等等。这样的接口是无法面向所有的应用的。不过Java内置了观察者模式相关的接口,可以在下一篇看到相关的实现。
原文地址:https://www.cnblogs.com/longshaohang/p/3355733.html