观察者模式定义了一系列对象之间的一对多关系。当一个对象改变状态,其它依赖者都会收到通知。
本文例子及思路来源于《Head First 设计模式》
场景
  我们接到一个来自气象局的需求:气象局需要我们构建一套系统,这系统有两个公告牌,分别用于显示当前的实时天气和未来几天的天气预报。当气象局发布新的天气数据(WeatherData)后,两个公告牌上显示的天气数据必须实时更新。气象局同时要求我们保证程序拥有足够的可扩展性,因为后期随时可能要新增新的公告牌。
概况
  这套系统主要包括三个部分:气象站(获取天气数据的物理设备)、WeatherData(接收气象站数据,并当数据有更新时提示公告牌进行更新)、公告牌(用于展示天气数据)。
WeatherData知道如何跟气象站联系,以获得天气数据。当天气数据有更新时,WeatherData会更新两个公告牌用于展示新的天气数据。
错误示范
在未接触观察者模式前,我写的代码可能如下所示:
1 2 3 4 5 6 7 8 9 10 11 12
| public class WeatherData { ... public void measurementsChanged(){ String weather = getWeather(); Double degree = getDegree(); currentConditionsDisplay.update(weather, degree); forecastDisplay.update(weather, drgee); } }
|
这个代码是典型的针对具体实现编程,这回导致我们再添加或删除公告牌时必须修改程序。接下来我们来看看观察者模式如何实现这一功能。
观察者模式介绍
  观察者模式面向的需求是:A对象(观察者)对B对象(被观察者)的某种变化高度敏感,需要在B变化的时候做出反应。例如报社与客户,客户向报社订阅了报纸。只要有新报纸出版,报社就会给你送来新报纸。而当客户取消订阅后,此项服务变会终止。当客户再次订阅后,则又会有新的报纸推送。
  观察者模式通常基于Subject
和Observer
接口来设计,下面是类图:
  Subject
是主题接口,registerObverser使其他对象注册为此主题的观察者(订阅),removeObverser把某对象从观察者中剔除(取消订阅)。ConcerteSubject
一个具体主题总是实现主题接口,除了订阅和取消订阅方法外,具体主题还实现了notifyObserver()方法,此方法用于在状态改变时更新所有观察者。
  所有观察者必须实现观察者接口Observer
,这个接口只有一个update方法,当主题改变时它被调用。ConcerteObserver
可以是实现此接口的任意类,观察者必须订阅具体主题,以便接收更新。
观察者模式的应用
  结合上面的类图,我们可以将观察者模式应用到WeatherData项目中来。设计类图如下:
代码实现如下:
主题接口:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
|
public interface Subject {
void registerObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers();
}
|
具体WeatherData主题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| public class WeatherData implements Subject {
private List<Observer> observers; private String weather; private Double drgee; public WeatherData() { this.observers = new ArrayList<>(); } @Override public void registerObserver(Observer observer) { if (observer != null){ observers.add(observer); } } @Override public void removeObserver(Observer observer) { Iterator<Observer> observerIterator = observers.iterator(); while (observerIterator.hasNext()){ Observer o = observerIterator.next(); if (o.equals(observer)){ observerIterator.remove(); } } }
@Override public void notifyObservers() { Iterator<Observer> observerIterator = observers.iterator(); while (observerIterator.hasNext()){ Observer observer = observerIterator.next(); observer.update(weather, drgee); } }
public void measurementsChanges(String weather, Double degree){ this.weather = weather; this.drgee = degree; notifyObservers(); } }
|
观察者接口
1 2 3 4 5 6 7 8 9 10
| public interface Observer {
void update( String weather, Double degree);
}
|
具体观察者
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| public class DegreeDisplayElement implements Observer, DisplayElement {
private Double degree; private Subject subject; public DegreeDisplayElement(Subject subject) { this.subject = subject; subject.registerObserver(this); } @Override public void diplay() { System.out.println("温度公告牌显示当前温度为:" + this.degree); } @Override public void update(LocalDate localDate, String weather, Double degree) { System.out.println("温度公告牌更新"); this.degree = degree; diplay(); }
}
|