GoF23:Observer-观察者
1、观察者模式
观察者(Observer)
-
含义:定义对象之间的一对多依赖,当对象状态改变时,所有依赖者会收到通知并自动更新。
-
相关概念:
- 主题(Subject):被观察对象,可以注册观察者对象列表,当状态发生改变时通知观察者。
- 观察者(Observer):感知到主题状态发生改变时,自动调用相应方法。
-
说明:遵循开闭原则。
类图
应用
- java.util
- Swing、GUI框架
- JavaBeans
- RMI
2、自定义方式实现
需求:已知一台测量气象数据的装置,
要求设计一个 WeatherData 和 Display 对象。
-
WeatherData:能获取气象站数据,发生数据更新时更新显示板内容。
-
Display:显示气象数据,且支持自定义显示板。
分析:WeatherData 是主题,Display 是观察者。
2.1、定义接口
观察者 - Observer
用于更新气象数据
public interface Observer {
void update(double temperature,
double humidity,
double pressure);
}
主题 - Subject
提供注册、删除通知观察者的方法。
public interface Subject {
void addObserver(Observer o);
void deleteObserver(Observer o);
void notifyObservers();
}
业务接口
业务功能
public interface Display {
void display();
}
2.2、实现类
观察者 - ConditionsDisplay
实现观察者接口和业务接口
-
成员变量:
- 主题状态:温度、湿度、气压。
- 对主题接口的引用。
-
接口方法:
- 观察者方法:update(),更新业务状态。
- 业务方法:显示气象信息。
-
扩展方法:如 cancelObserver(),主动取消注册。
public class ConditionsDisplay implements Observer, Display { // 主题状态 private double temperature; private double humidity; private double pressure; // 主题 private final Subject subject; public ConditionsDisplay(Subject subject) { this.subject = subject; // 注册观察者 subject.addObserver(this); } @Override public void update(double temperature, double humidity, double pressure) { this.temperature = temperature; this.humidity = humidity; this.pressure = pressure; display(); } @Override public void display() { System.out.println("=====Current Conditions====="); System.out.println("temperature(℃)\t" + temperature); System.out.println("humidity(%) \t" + humidity); System.out.println("pressure(hPa) \t" + pressure); } // public void cancelObserver(){ // subject.deleteObserver(this); // } }
主题 - WeatherData
实现主题接口
-
成员变量:
- 主题状态:温度、湿度、气压。
- 观察者列表。
-
接口方法:主题方法
- 注册观察者
- 移除观察者
- 通知观察者
-
业务方法:更新主题状态(气象),通知观察者。
public class WeatherData implements Subject { // 主题状态 private double temperature; private double humidity; private double pressure; // 观察者列表 private List<Observer> observerList; public WeatherData() { observerList = new ArrayList<>(); } @Override public void addObserver(Observer o) { if (o != null) { observerList.add(o); } } @Override public void deleteObserver(Observer o) { int i = observerList.indexOf(o); if (i >= 0) { observerList.remove(i); } } @Override public void notifyObservers() { for (Observer observer : observerList) { observer.update(temperature, humidity, pressure); } } /** * 更新气象数据 */ public void updateMeasurements(double temperature, double humidity, double pressure) { this.temperature = temperature; this.humidity = humidity; this.pressure = pressure; notifyObservers(); } }
3、Java API 实现
数据传递方式
推(push)🔥 | 拉(pull) | |
---|---|---|
含义 | Subject 主动将数据传递给 Observer | Observer 主动获取 Subject 的数据 |
优点 | Observer 无需多次获取数据 | Observer 可以按需获取数据 |
缺点 | 通常只能 push 所有数据,无法按需推送 | 无法一次性获取所有状态 |
观察者模式 Java API(支持 push 和 pull)
- 主题:java.util.Observable
- 观察者:java.util.Observer
3.1、观察者接口 - Observer
-
o:传送数据的主题。
-
arg:数据。
public interface Observer { void update(Observable o, Object arg); }
3.2、主题类 - Observable
重要方法
-
changed:标识主题状态是否已改变。
- setChanged()
- clearChanged()
- hasChanged()
-
notifyObservers():通知观察者(重载方法)
-
判断主题状态已改变才通知。
-
恢复状态,表示当前通知之后没有其它改变。
-
通知所有观察者。
// pull方式:需要观察者自行获取主题数据 public void notifyObservers() { notifyObservers(null); } // push:主题将指定数据agr发给观察者 public void notifyObservers(Object arg) { Object[] arrLocal; // 判断 synchronized (this) { if (!changed){ return; } arrLocal = obs.toArray(); // 设值 clearChanged(); } // 通知 for (int i = arrLocal.length-1; i>=0; i--) ((Observer)arrLocal[i]).update(this, arg); }
-
分析
Observable 是 class,而不是 interface。
- 只能通过继承 Observable 来成为主题。
- Java 单继承限制了 Observable 的复用性,如无法同时继承 Observable 和另一个重要超类。
- 违反合成复用原则。