一、引言
什么是观察者模式呢?其实生活中很常见:报社出版报纸,并定期向订阅用户推送报纸,用户可以订阅报纸和取消订阅。这个例子中可以看出观察者模式的主体有两个,即:出版者+订阅者,我们改个名称:出版者改为“主题”,订阅者改为“观察者”。
二、观察者模式
定义:定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新
意图:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
主要解决:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。
何时使用:一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。
如何解决:使用面向对象技术,可以将这种依赖关系弱化。
关键代码:在抽象类里有一个 ArrayList 存放观察者们。
三、实现
实例剖析:一个气象监测应用,气象监测系统定时会更新气象的相关数据,而对应有三种布告板都要显示此信息(显示的形式有所不同),一旦气象数据有所改动,三块布告板的数据也要同时的更新布告板中的显示信息。
分析:从需求中很容易看出:气象数据是主题,而三块布告板是观察者。要实现管理订阅者,则主题中必须存在一个数组列表用户管理。有一个方法进行数据推送更新,而订阅者的实现比较简单,他们继承统一的订阅接口,并且有一个更新数据方法。
类图:
代码实现:
主题及观察者接口
// 观察者接口public interface Observer { public void update(float temp, float humidity, float pressure);}//主题接口public interface Subject { //注册观察者 void registerObserver(Observer o); //移除观察者 void removeObserver(Observer o); //通知观察者 void notifyObservers();}//布告板接口public interface DisplayElement { void display();}
主题实现
//主题实现public class WeatherData implements Subject { //订阅者列表 private ArrayList observers; private float temperature; private float humidity; private float pressure; public WeatherData(){ observers=new ArrayList(); } //增加订阅者 public void registerObserver(Observer o) { observers.add(o); } //移除订阅者 public void removeObserver(Observer o) { int index=observers.indexOf(o); if(index>=0) { observers.remove(index); } } //通知订阅者 public void notifyObservers() { for (int i = 0; i
观察者实现
public class CurrentConditionsDisplay implements DisplayElement,Observer { private float temperature; private float humidity; private Subject weatherData; public CurrentConditionsDisplay(Subject weatherData) { this.weatherData = weatherData; weatherData.registerObserver(this); } public void display() { System.out.println("第一种显示方式: " + temperature + "F degrees and " + humidity + "% humidity"); } public void update(float temp, float humidity, float pressure) { this.temperature = temp; this.humidity = humidity; display(); }}
public class StatisticsDispxlay implements DisplayElement,Observer { private float temperature; private float pressure; private Subject weatherData; public StatisticsDispxlay(WeatherData weatherData){ this.weatherData=weatherData; } public void display() { System.out.println("第二种显示方式: " + temperature + "F degrees and " + pressure + "% pressure"); } public void update(float temp, float humidity, float pressure) { this.temperature=temp; this.pressure=pressure; }}
public class ForecastDisplay implements DisplayElement,Observer { private float humidity; private float pressure; private WeatherData weatherData; public ForecastDisplay(WeatherData weatherData){ this.weatherData=weatherData; } public void display() { System.out.println("第三种显示方式: " + humidity + "F degrees and " + pressure + "% pressure"); } public void update(float temp, float humidity, float pressure) { this.humidity=humidity; this.pressure=pressure; }}
运行:
private static void observer() { WeatherData weatherData = new WeatherData(); CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData); StatisticsDispxlay statisticsDisplay = new StatisticsDispxlay(weatherData); ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData); weatherData.setMeasurements(80, 65, 30.4f); weatherData.setMeasurements(82, 70, 29.2f); weatherData.setMeasurements(78, 90, 29.2f); }
运行结果:
四、优缺点
观察者模式有以下几个优点:
- 观察者模式实现了表示层和数据逻辑层的分离,并定义了稳定的更新消息传递机制,并抽象了更新接口,使得可以有各种各样不同的表示层,即观察者。
- 观察者模式在被观察者和观察者之间建立了一个抽象的耦合,被观察者并不知道任何一个具体的观察者,只是保存着抽象观察者的列表,每个具体观察者都符合一个抽象观察者的接口。
- 观察者模式支持广播通信。被观察者会向所有的注册过的观察者发出通知。
观察者也存在以下一些缺点:
- 如果一个被观察者有很多直接和间接的观察者时,将所有的观察者都通知到会花费很多时间。
- 虽然观察者模式可以随时使观察者知道所观察的对象发送了变化,但是观察者模式没有相应的机制使观察者知道所观察的对象是怎样发生变化的。
- 如果在被观察者之间有循环依赖的话,被观察者会触发它们之间进行循环调用,导致系统崩溃,在使用观察者模式应特别注意这点。
结语:java中有内置的观察者模式,java.util包(package)内包含最基本的Observer接口与Observable类,这和我们的Subject接口与Observer接口很相似。里面还增加了状态的判断,这样可以让订阅者能够自主的拉取。