OOP设计模式[JAVA]——02观察者模式
观察者模式
观察者模式的设计原则
为交互对象之间的松耦合设计而努力,使对象之间的相互依赖降到最低。
观察者模式也是对象行为型模式,其意图为:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时, 所有依赖于它的对象都得到通知并被自动更新。其别名又叫:依赖(Dependents), 发布-订阅( Publish-Subscribe)
观察者模式的结构
参与者
- Subject(目标)——目标知道它的观察者,可以有任意多的观察者观察同一个目标
——提供注册和删除观察者对象的接口。 - Observer(观察者)——为那些在目标发生改变时需获得通知的对象定义一个更新接口。
- ConcreteSubject(具体目标)——将有关状态存入各具体观察者对象
——当它的状态发生改变时,向它的各个观察者发出通知。 - ConcreteObserver(具体观察者)——维护一个指向具体目标对象的引用
——存储有关状态,这些状态应与目标的状态保持一致
——实现Observer的更新接口以使自身状态与目标状态保持一致。
协作
- 当ConcreteSubject发生任何可能导致其观察者与其本身状态不一致的改变时,它将通知它的各个观察者。
- 在得到一个具体目标的改变通知后,ConcreteObserver对象可向目标对象查询信息。ConcreteObserver使用这些信息以使它的状态与目标对象的状态一致。
注意发出改变请求的Observer对象并不立即更新,而是将其推迟到它从目标得到一个通知之后。Notify不总是由目标对象调用,它也可被一个观察者或其它对象调用。
使用场景
这里我们来看一下HeadFirstDesignPattern中的例子,气象监测应用:
此系统由三个部分组成:气象站(获取实际数据的物理装置)、WeatherData对象(追踪来自气象站的数据,并更新布告板)和布告板(显示目前天气状况给用户)。其中WeatherData对象知道如何跟物理气象站联系并取得数据。WeatherData对象会随即更新三个布告板的显示:目前状况(温度、湿度和气压)、气象统计和天气预报。
现在我们可以来看具体的实现:
package headfirst.observer.weather; public interface Subject { public void registerObserver(Observer o); public void removeObserver(Observer o); public void notifyObserver(); }
package headfirst.observer.weather; public interface Observer { public void update(float temp, float humidity, float pressure); }
package headfirst.observer.weather; public interface DisplayElement { public void display(); }
//WeatherData对象
package headfirst.observer.weather; import java.util.ArrayList; public class WeatherData implements Subject { private ArrayList<Observer> observers; private float temperature; private float humidity; private float pressure; public WeatherData() { observers = new ArrayList<Observer>(); } @Override public void registerObserver(Observer o) { // TODO Auto-generated method stub observers.add(o); } @Override public void removeObserver(Observer o) { // TODO Auto-generated method stub int i = observers.indexOf(o); if (i >= 0) { observers.remove(i); } } @Override public void notifyObserver() { // TODO Auto-generated method stub for (int i = 0; i < observers.size(); i++) { Observer observer = (Observer)observers.get(i); observer.update(temperature, humidity, pressure); } } public void measurementsChanged() { notifyObserver(); } public void setMeasurements(float temp, float humidity, float pressure) { this.temperature = temp; this.humidity = humidity; this.pressure = pressure; measurementsChanged(); } }
//CurrentConditionsDisplay
package headfirst.observer.weather; public class CurrentConditionsDisplay implements Observer, DisplayElement { private float temperature; private float humidity; public CurrentConditionsDisplay(Subject weatherData) { weatherData.registerObserver(this); } @Override public void display() { // TODO Auto-generated method stub System.out.println("Current conditions: " + temperature + "F degrees and " + humidity + "% humidity"); } @Override public void update(float temp, float humidity, float pressure) { // TODO Auto-generated method stub this.temperature = temp; this.humidity = humidity; display(); } }
//ForecastDisplay
package headfirst.observer.weather; public class ForecastDisplay implements Observer, DisplayElement { private float currentPressure = 29.92f; private float lastPressure; private WeatherData weatherData; public ForecastDisplay(WeatherData weatherData) { this.weatherData = weatherData; weatherData.registerObserver(this); } public void update(float temp, float humidity, float pressure) { lastPressure = currentPressure; currentPressure = pressure; display(); } public void display() { System.out.print("Forecast: "); if (currentPressure > lastPressure) { System.out.println("Improving weather on the way!"); } else if (currentPressure == lastPressure) { System.out.println("More of the same"); } else if (currentPressure < lastPressure) { System.out.println("Watch out for cooler, rainy weather"); } } }
//StatisticsDisplay
package headfirst.observer.weather; public class StatisticsDisplay implements Observer, DisplayElement { private float maxTemp = 0.0f; private float minTemp = 200; private float tempSum= 0.0f; private int numReadings; private WeatherData weatherData; public StatisticsDisplay(WeatherData weatherData) { this.weatherData = weatherData; weatherData.registerObserver(this); } public void update(float temp, float humidity, float pressure) { tempSum += temp; numReadings++; if (temp > maxTemp) { maxTemp = temp; } if (temp < minTemp) { minTemp = temp; } display(); } public void display() { System.out.println("Avg/Max/Min temperature = " + (tempSum / numReadings) + "/" + maxTemp + "/" + minTemp); } }
效果
Observer模式允许你独立的改变目标和观察者。你可以单独复用目标对象而无需同时复用其观察者, 反之亦然。它也使你可以在不改动目标和其他的观察者的前提下增加观察者。
下面是观察者模式其它一些优缺点:
- 目标和观察者之间的抽象耦合 一个目标所知道的仅仅是它有一系列观察者, 每个都符合抽象的Observer类的简单接口。目标不知道任何一个观察者属于哪一个具体的类。这样目标和观察者之间的耦合是抽象的和最小的。因为目标和观察者不是紧密耦合的,它们可以属于一个系统中的不同抽象层次。一个处于较低层次的目标对象可与一个处于较高层次的观察者通信并通知它,这样就保持了系统层次的完整。如果目标和观察者混在一块,那么得到的对象要么横贯两个层次(违反了层次性),要么必须放在这两层的某一层中(这可能会损害层次抽象)。
- 支持广播通信 不像通常的请求,目标发送的通知不需指定它的接收者。通知被自动广播给所有已向该目标对象登记的有关对象。目标对象并不关心到底有多少对象对自己感兴趣;它唯一的责任就是通知它的各观察者。这给了你在任何时刻增加和删除观察者的自由。处理还是忽略一个通知取决于观察者。
- 意外的更新 因为一个观察者并不知道其它观察者的存在,它可能对改变目标的最终代价一无所知。在目标上一个看似无害的的操作可能会引起一系列对观察者以及依赖于这些观察者的那些对象的更新。此外, 如果依赖准则的定义或维护不当,常常会引起错误的更新, 这种错误通常很难捕捉。简单的更新协议不提供具体细节说明目标中什么被改变了,这就使得上述问题更加严重。如果没有其他协议帮助观察者发现什么发生了改变,它们可能会被迫尽力减少改变。