《Head First 设计模式》之观察者模式——天气显示

观察者模式(Observer)

  ——在对象之间定义一对多的依赖,这样一来,当一个对象改变状态,依赖它的对象都会收到通知,并自动更新。

(出版者Subject+订阅者Observer=观察者模式)

  • 特点:定义并维护对象之间的一对多关系
  • 原则:为交互对象之间的松耦合设计而努力
  • 示例(气象站类图)

三个接口:

 1 public interface Subject{
 2     public void registerObserver(Observer o);//注册观察者
 3     public void removeOberver(Observer o);//删除观察者
 4     public void notifyObserver();//通知观察者
 5 }
 6 
 7 public interface Observer{
 8     public void update(float temp, float humidity, float pressure);
 9 }
10 
11 public interface DisplayElement{
12     public void display();
13 }

在WeatherData中实现主题接口:

 1 public class WeatherData implements Subject{
 2     private ArrayList observers;//记录观察者
 3     private float temperature;
 4     private float humidity;
 5     private float pressure;
 6     
 7     public WeatherData(){
 8         observers = new ArrayList();
 9     }
10     
11     public void registerObserver(Observer o){//注册观察者
12         observers.add(o);
13     }
14     public void removeOberver(Observer o){//删除观察者
15         int i = observers.indexOf(o);
16         if(i >= 0){
17             observers.remove(0);
18         }
19     }
20     public void notifyObservers(){
21         for(int i = 0; i < observers.size(); i++){ //通知每一个观察者
22             Observer observer = (Observer)observers.get(i);
23             observer.update(temperature, humidity, pressure);
24         }
25     }
26     public void measurementsChanged(){
27         notifyObservers();
28     }
29     public void setMeasurements(float temp, float hum, float pre){
30         this.temperature = temp;
31         this.humidity = hum;
32         this.pressure = pre;
33         measurementsChanged();
34     }
35 }

实现CurrentConditionDisplay.java

 1 public class CurrentConditionsDisplay implements Observer, DisplayElement{
 2     private float temperature;
 3     private float humidity;
 4     private Subject weatherData;
 5     
 6     public CurrentConditionsDisplay(Subject weatherData){//构造器需要weatherData对象作为注册之用
 7         this.weatherData = weatherData;
 8         weatherData.registerObserver(this);
 9     }
10     public void update(float temp, float humidity, float pressure){
11         this.temperature = temp;
12         this.humidity = humidity;
13         display();
14     }
15     public void display(){
16         //输出
17     }
18 }

测试程序:

 1 public class WeatherStation {
 2 
 3         public static void main(String[] args) {
 4             WeatherData weatherData = new WeatherData();//建立weatherData对象
 5 
 6             CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData);
 7             StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData);
 8             ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData);//将对象传给三个Observer,即add观察者
 9 
10             weatherData.setMeasurements(80, 65, 30.4f);
11             weatherData.setMeasurements(82, 70, 29.2f);
12             weatherData.setMeasurements(78, 90, 29.2f);
13         }
14     }

 


 

Java内置的观察者模式

  上述示例实现信息由Subject “推送(push)” 至Observer,使用Java内置的观察者模式可以使用推(push)或拉(pull)的方式传送数据。不同的是WeatherData现在扩展自Observable类,并继承到一些add、delete、notify观察者的方法。

Subject -> java.util.Observable(类)

Observer -> java.util.Observer(接口)

Observable如何送出通知?

  首先需要利用扩展 java.util.Observable 接口产生“可观察者”类(想要进行通知,则必须调用Observable类的setChanged方法,但是Observable的setChanged方法为protected,故只能使用继承来实现自己的主题对象),然后:

  1. 调用setChanged(),标记状态已经改变的事实;
  2. 调用notifyObservers()中的一个:notifyObservers() 或 notifyObservers(Object arg)

Observer如何接收通知?

update(Observable o, Object arg)  :主题Observable作为第一变量,好让观察者知道是哪个主题通知它的。Object arg正是传入notifyObservers(Object arg)的数据对象,如果没有说明则为空。

若想push数据给观察者,可以把数据作数据对象传送给notifyObservers(Object arg)方法。否则,观察者就必须从可观察者对象中pull数据。如何拉数据?

 WeatherData.java

 1 public class WeatherData extends Observable{
 2     private float temperature;
 3     private float humidity;
 4     private float pressure;
 5     
 6     public WeatherData(){ } //无需建立观察者列表ArrayList了
 7     public void measurementsChanged(){
 8         setChanged();//状态已经改变
 9         notifyObservers();//pull
10     }
11     public void setMeasurements(float temp, float hum, float pre){
12         this.temperature = temp;
13         this.humidity = hum;
14         this.pressure = pre;
15         measurementsChanged();
16     }
17     public float getTemperature(){
18         return temperature;
19     }
20     public float gethumidity(){
21         return humidity;
22     }
23     public float getpressure(){
24         return pressure;
25     }
26 }

CurrentConditionDisplay.java

 1 public class CurrentConditionsDisplay implements Observer, DisplayElement{
 2     private float temperature;
 3     private float humidity;
 4     Observable observable;
 5     
 6     public CurrentConditionsDisplay(Observable observable){
 7         this.observable = observable;
 8         observable.addObserver(this);
 9     }
10     public void update(Observable o, Object arg){
11         if(o instanceof WeatherData){
12             WeatherData weatherData = (WeatherData)o;
13             this.temperature = weatherData.getTemperature();
14             this.humidity = weatherData.gethumidity();
15             display();
16         }
17     }
18     public void display(){
19         //输出
20     }
21 }

notice:

  1. 不想推送的时候,不调用setChanged()方法即可
  2. 通知顺序不依赖于注册的顺序(即主题通知观察者的顺序与添加观察者的顺序无关)
  3. setChanged()方法的必要性:若无,则温度计读数每十分之一度就会更新,造成WeatherData对象持续不断地通知观察者。若希望温差达到半度时更新,就调用setChanged()。有更多的弹性,更适当地通知观察者。

使用Java自带的观察者模式的缺点:

  1. Observable是一个类,而不是一个接口,导致Observable类的扩展性不高,不如自己实现的观察者模式灵活
  2. Observable将某些方法保护了起来(setChanged()和clearChanged()为protected),这意味着除非继承自Observable,否则将有关键的方法不能调用。导致无法通过组合的方式使其它类获得Observable类的功能。违反了设计原则“多用组合,少用继承”。

 

posted @ 2017-02-23 19:20  D-Dong  阅读(850)  评论(0编辑  收藏  举报