观察者模式(Observer Pattern):观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。
推拉两种获取数据方式:
- 推:可以把数据当做数据对象的方式传给观察者
- 拉:通过getter方法获取数据
设计原则:
- 为了交互对象之间的松耦合而努力,让我们建立有弹性的OO系统
特点:
- 观察者模式定义了对象之间的一对多关系
- 当一个对象的状态改变了,其他的依赖者都会收到通知
松耦合的威力:
- 两个对象之间松耦合,他们可以交互,但是不太清楚彼此的细节
- 观察者模式提供了一种对象设计,让主题和观察者之间松耦合
- 主题只知道观察者对象实现了某个借口,而不知道观察者具体的细节
- 我们可以随时添加和删除观察者
适用性
- 当一个抽象模型有两个方面,其中一个方面依赖于另一方面。将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。
- 当对一个对象的改变需要同时改变其它对象, 而不知道具体有多少对象有待改变。
- 当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换言之,你不希望这些对象是紧密耦合的。
举例:
- 实现一个气象监测系统,此系统的三个部分是气象站(获取实际气象数据的物理装置)、WeatherData对象(追踪来自气象站的数据,并更新布告板)和布告板(显示目前天气状况给用户看)
- 我们可以通过MeasurementsChanged()方法通知各个布告版
-
1 public void MeasurementsChanged() 2 { 3 float temp = GetTemperature(); 4 float humidity = GetHumidity(); 5 float pressure = GetPressure(); 6 //三个布告版 7 currentConditionDisplay.update(temp, humidity, pressure); 8 statisticsDisplay.update(temp, humidity, pressure); 9 forecastDisplay.update(temp, humidity, pressure); 10 }
-
- 然而布告版的数量是不确定的,可能在某个时刻增加,可能在某个时刻减少,而且种类不同,如果使用如上代码,当布告版变化时代码修改维护量巨大
- 使用观察者模式修改设计
代码实现
1、主题接口
-
1 public interface Subject { 2 public void registerObserver(Observer o); 3 public void removeObserver(Observer o);
4 public void notifyObservers(); 5 }
2、观察者接口
1 public interface Observer { 2 public void update(float temp, float humidity, float pressure); 3 }
3、主题实现
1 public class WeatherData implements Subject { 2 private ArrayList observers; 3 //temperature、humidity、pressure就是WeatherData的“状态” 4 private float temperature; 5 private float humidity; 6 private float pressure; 7 8 public WeatherData() { 9 observers = new ArrayList(); 10 } 11 12 public void registerObserver(Observer o) { 13 observers.add(o); 14 } 15 16 public void removeObserver(Observer o) { 17 int i = observers.indexOf(o); 18 if (i >= 0) { 19 observers.remove(i); 20 } 21 } 22 23 public void notifyObservers() { 24 for (int i = 0; i < observers.size(); i++) { 25 Observer observer = (Observer)observers.get(i); 26 observer.update(temperature, humidity, pressure); 27 } 28 } 29 30 public void measurementsChanged() { 31 notifyObservers(); 32 } 33 34 public void setMeasurements(float temperature, float humidity, float pressure) { 35 this.temperature = temperature; 36 this.humidity = humidity; 37 this.pressure = pressure; 38 measurementsChanged(); 39 } 40 41 public float getTemperature() { 42 return temperature; 43 } 44 45 public float getHumidity() { 46 return humidity; 47 } 48 49 public float getPressure() { 50 return pressure; 51 } 52 }
4、观察者实现
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) { 7 this.weatherData = weatherData; 8 weatherData.registerObserver(this); 9 } 10 //推数据,这里只使用到了前两个参数 11 public void update(float temperature, float humidity, float pressure) { 12 this.temperature = temperature; 13 this.humidity = humidity; 14 display(); 15 } 16 17 public void display() { 18 System.out.println("Current conditions: " + temperature 19 + "F degrees and " + humidity + "% humidity"); 20 } 21 }
要点
- 观察者模式定义了对象之间一对多的关系。
- 主题(也就是可观察者)用一个共同的接口来更新观察者。
- 观察者和可观察者之间用松耦合方式结合,可观察者不知道观察者的细节,只知道观察者实现了观察者接口。
- 使用此模式时,你可从被观察者处推或拉数据。
- 有多个观察者时,不可以依赖特定的通知次序。
- Java有多种观察者模式的实现,包括了通用的java.util.Observable。
- 要注意java.util.Observable实现上所带来的一些问题。Swing大量使用观察者模式,许多GUI框架也是如此。
- Java内置的观察者模式中Observable是一个类,你必须设计一个类去继承它。如果某类相同时具有Observable类和另一个超类的行为,就无法实现,因为java不支持多继承。
- Observable API将setChanged()方法保护了起来,除非继承自Observable类,否则无法创建Observable实例组合到自己的对象中,也违背了面向对象设计的第二个原则:多用组合,少用继承。
- 此模式也被应用在许多地方,例如:JavaBeans、RMI。