第二式 观察者模式 观察者模式

一 气象站应用项目

  这天公司接到一个气象站项目,气象站通过自己的各类检测器获得的数据打包提供给我们,我们来帮他们实现在不同的布告板上展示,可以显示目前状况,气象统计及简单预报。并且还希望我们能公布一组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标志,通过这些可以让主题更灵活的选择是否向观察者发送数据。