观察者模式
一 气象站应用项目
这天公司接到一个气象站项目,气象站通过自己的各类检测器获得的数据打包提供给我们,我们来帮他们实现在不同的布告板上展示,可以显示目前状况,气象统计及简单预报。并且还希望我们能公布一组API,好让其他的开发者写出自己的布告板。
项目概况:我们建立一个应用,利用WeatherData对象取得数据,并更新三个布告板:目前情况、气象统计和天气预报。
二 认识观察者模式
在开始着手上面的项目前,我们来认识一下观察者模式。相信大家都有过订阅报纸或者订牛奶的经历,我们来看看是怎么个过程。
首先,报社的任务是出版报纸,用户向报社订阅报纸,只要他们有新报纸出版就会送给你,如果你不想看了,取消订阅,他们就不会再送了;只要报社还没破产倒闭就一直存在订阅和取消订阅的行为。
出版者 + 订阅者 = 观察者模式。观察者模式中出版者称作主题,订阅者称作观察者。
观察者模式:定义了对象之间的一对多依赖,这样一来,一个对象改变状态时,他的所有依赖者都会收到通知并自动更新。
UML图如下:
设计原则:为了交互对象之间的松耦合设计而努力。
观察者模式提供了一种对象设计,让主题和观察者之间松耦合。关于观察者的一切,主题只知道他实现了观察者接口,主题不知道他具体是谁,做了哪些细节上的事。
我们可以在任何时候增加新的观察者,而不需要修改主题的代码;同样也可以删掉旧的观察者。我们可以在其他地方独立的复用主题或观察者,因为他们之间并非紧耦合。
松耦合的设计之所以能让我们建立弹性的OO系统,能够应对变化,是因为对象之间的相互依赖降到了最低。
三 气象站代码实现
UML类图:
代码:
1.创建主题、观察者、布告展示接口;天气数据封装到Data类
1 public interface Subject { 2 public void registerObserver(Observer o); 3 public void removeObserver(Observer o); 4 public void notifyObservers(); 5 } 6 public interface Observer { 7 public void update(Data data); 8 } 9 10 public interface DisplayElement { 11 public void display(); 12 } 13 public class Data { 14 private float temp; 15 private float humidity; 16 private float pressure; 17 18 public Data(float temp, float humidity, float pressure) { 19 this.temp = temp; 20 this.humidity = humidity; 21 this.pressure = pressure; 22 } 23 24 @Override 25 public String toString() { 26 return "Data{" + 27 "temp=" + temp + 28 ", humidity=" + humidity + 29 ", pressure=" + pressure + 30 '}'; 31 } 32 33 public float getTemp() { 34 return temp; 35 } 36 37 public void setTemp(float temp) { 38 this.temp = temp; 39 } 40 41 public float getHumidity() { 42 return humidity; 43 } 44 45 public void setHumidity(float humidity) { 46 this.humidity = humidity; 47 } 48 49 public float getPressure() { 50 return pressure; 51 } 52 53 public void setPressure(float pressure) { 54 this.pressure = pressure; 55 } 56 }
2.实现主题接口的天气数据主题类
1 public class WeatherData implements Subject{ 2 private List<Observer> list; 3 private Data data; 4 5 public WeatherData(){ 6 list = new ArrayList<Observer>(); 7 } 8 9 public void registerObserver(Observer o) { 10 //注册观察者时,将观察者添加到list中 11 list.add(o); 12 } 13 14 public void removeObserver(Observer o) { 15 //观察者取消订阅时 16 list.remove(o); 17 } 18 19 public void notifyObservers() { 20 //通知所有已注册观察者 21 for(Observer o : list){ 22 o.update(data); 23 } 24 } 25 26 public void dataChanged(){ 27 //气象站数据变更后,通知观察者 28 this.notifyObservers(); 29 } 30 31 public void setData(Data data){ 32 this.data = data; 33 dataChanged(); 34 } 35 }
3.两种布告板观察者
1 public class CurrentConditionDisplay implements Observer,DisplayElement { 2 private Subject weatherData; 3 private Data data; 4 5 public CurrentConditionDisplay(Subject weatherData){ 6 this.weatherData = weatherData; 7 weatherData.registerObserver(this); 8 } 9 public void display() { 10 System.out.println("CurrentConditionDisplay:"+this.data); 11 } 12 13 public void update(Data data) { 14 this.data = data; 15 display(); 16 } 17 } 18 public class StaticsDisplay implements Observer,DisplayElement { 19 private Subject weatherData; 20 private Data data; 21 22 public StaticsDisplay(Subject weatherData){ 23 this.weatherData = weatherData; 24 weatherData.registerObserver(this); 25 } 26 public void display() { 27 System.out.println("StaticsDisplay:"+this.data); 28 } 29 30 public void update(Data data) { 31 this.data = data; 32 display(); 33 } 34 }
4.测试类
1 public class Test { 2 public static void main(String[] args){ 3 WeatherData weatherData = new WeatherData(); 4 CurrentConditionDisplay display1 = new CurrentConditionDisplay(weatherData); 5 StaticsDisplay display2 = new StaticsDisplay(weatherData); 6 weatherData.setData(new Data(12,22,33.1f)); 7 weatherData.setData(new Data(2.3f,42,13)); 8 } 9 }
5.测试结果如下
四 Java内置的观察者模式
现在,都是主题数据有更新时,一股脑把所有数据都传给观察者了,观察者可能不需要这么多数据,他可能想自己来拉取数据。我们来看看java内置的观察者模式。
Java内置的观察者模式运作方式,和我们在气象站中的实现类似,但有一些小差异。最明显的差异是WeatherData现在扩展自Observable类,并继承到一些增加,删除,
通知观察者的方法。Observable类中,有一个setChanged方法,及changed标志,通过这些可以让主题更灵活的选择是否向观察者发送数据。