观察者模式:定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。
有这样一个需求:
一个气象站,它会提供气温,气压,湿度等气象数据,要实现一个显示当前气象状态的显示装置。
能显示当前气温、气压、湿度等。当气象站的数据一发生改变,就能通知显示装置。
使用观察者模式实现。
先看看观察者模式类图:
主题是一个接口,定义了注册观察者、删除观察者、通知方法
package com.headfirst.chapter2; public interface Subject {//主题 public void registerObserver(Observer o);//添加观察者 public void removeObserver(Observer o);//删除观察者 public void notifyObserver();//通知观察者 }
观察者也是一个接口,主题的notifyObserver方法会调用观察者的update方法。
package com.headfirst.chapter2; public interface Observer {//观察者 public void update(float temp,float humidity,float pressure); }
根据上面的需求,我们实现具体的主题,代码如下:
package com.headfirst.chapter2; import java.util.ArrayList; import java.util.List; public class WeatherDataSubject implements Subject { private float temp;// 温度 private float humidity;// 温度 private float pressure;// 气压 private List observers;// 这个列表用于存入观察者 public WeatherDataSubject() { 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 notifyObserver() { for (int i = 0; i < observers.size(); i++) { Observer observer = (Observer) observers.get(i); observer.update(temp, humidity, pressure); //依次通知观察者 } } public void measurementChanged() { notifyObserver(); } public void setMeasurements(float temp, float humidity, float pressure) { this.temp = temp; this.humidity = humidity; this.pressure = pressure; measurementChanged(); } }
观察者
package com.headfirst.chapter2; public class CurrentConditionDisplay implements Observer { private float temp; private float humidity; private float pressure; public CurrentConditionDisplay(Subject weatherDataSubject) { weatherDataSubject.registerObserver(this); } public void update(float temp, float humidity, float pressure) { this.temp = temp; this.humidity = humidity; this.pressure = pressure; display(); } public void display() {//显示方法 System.out.println("temp = "+temp+",humidity = "+humidity+", pressure = "+pressure); } }
测试类
package com.headfirst.chapter2; public class Test { public static void main(String[] args) { WeatherDataSubject subject = new WeatherDataSubject(); Observer observer = new CurrentConditionDisplay(subject); subject.setMeasurements(32, 11, 420); } }
使用JAVA内置的观察者模式
JAVA的util包中提供了内置的观察者模式API,分别是Observable类和Observer接口。
它可以使用推或拉的方式传输数据。
其中setChanged()方法用来标识状态是否已经改变的事实,如果调用notifyObservers()方法前没有先调用setChanged方法,那么观察都就不会被通知。
setChanged方法可以在更新观察者时,有更多弹性,可以适当通知观察者,例如气象站很敏锐,每十分之一度就会更新,这会造成显示装置不断被通知。
我们可以通过setChanged方法将通知频度设为一度通知更新一次。
把上面的例子使用内置观察者模式重写一遍:如下
package com.headfirst.chapter2; import java.util.Observable; //主题 public class WeatherData extends Observable { private float temp;// 温度 private float humidity;// 温度 private float pressure;// 气压 public void measureChanged() { setChanged(); notifyObservers(); } public void setMeasurement(float temp, float humidity, float pressure) { this.temp = temp; this.humidity = humidity; this.pressure = pressure; measureChanged(); } public float getTemp() { return temp; } public float getHumidity() { return humidity; } public float getPressure() { return pressure; } }
package com.headfirst.chapter2; import java.util.Observable; import java.util.Observer; //观察者 public class ConditionDisplay implements Observer { private float temp; private float humidity; private float pressure; public ConditionDisplay(Observable o) { o.addObserver(this); } public void update(Observable arg0, Object arg1) { if(arg0 instanceof WeatherData){ WeatherData data = (WeatherData)arg0; this.temp = data.getTemp(); this.humidity = data.getHumidity(); this.pressure = data.getPressure(); display(); } } public void display(){ System.out.println("temp = "+temp+",humidity = "+humidity+", pressure = "+pressure); } }
测试类:
package com.headfirst.chapter2; import java.util.Observer; public class Test { public static void main(String[] args) { WeatherData data = new WeatherData(); Observer observer = new ConditionDisplay(data); data.setMeasurement(23, 411, 89); } }