Intent
定义对象之间的一对多依赖,当一个对象状态改变时,它的所有依赖都会收到通知并且自动更新状态。
主题(Subject)是被观察的对象,而其所有依赖者(Observer)称为观察者。
观察者模式具有以下4个角色:
- 抽象主题(Subject)角色:该角色又称为“被观察者”,可以增加和删除观察者对象。
- 抽象观察者(Observer)角色:该角色为所有的具体观察者定义一个接口,在得到主题的通知时更新自己。
- 具体主题(Concrete Subject)角色:该角色又称为“具体被观察者”,它将有关状态存入具体观察者对象,在具体主题的内部状态改变时,给所有登记过的观察者发出通知。
- 具体观察者(Concrete Observer)角色:该角色实现抽象观察者所要求的更新接口,以便使自身的状态与主题的状态相协调。
观察者模式具有以下几个优点:
- 观察者和被观察者之间是抽象耦合。被观察者角色所知道的只是一个具体观察者集合,每一个具体观察者都符合一个抽象观察者的接口。被观察者并不认识任何一个具体的观察者,它只知道它们都有一个共同的接口。由于被观察者和观察者没有紧密的耦合在一起,因此它们可以属于不同的抽象化层次,且都非常容易扩展。
- 支持广播通信。被观察者会向所有登记过的观察者发出通知,这就是一个触发机制,形成一个触发链。
观察模式的缺点如下:
- 如果一个主题有多个直接或间接的观察者,则通知所有的观察者会花费很多时间,且开发和调试都比较复杂。
- 如果在主题之间有循环依赖,被观察者会触发它们之间进行循环调用,导致系统崩溃。在使用观察者模式时要特别注意这一点。
- 如果对观察者的通知是通过另外的线程进行异步投递,系统必须保证投递的顺序执行。
- 虽然观察者模式可以随时使观察者知道所观察的对象发生了变化,但是观察者模式没有提供相应的机制使观察者知道所观察的对象是如何发生变化。
观察者模式的应用场景如下:
- 关联行为场景。
- 事件多级触发场景。
- 跨系统的消息交换场景,如消息队列的处理机制。
Class Diagram
主题(Subject)具有注册和移除观察者、并通知所有观察者的功能,主题是通过维护一张观察者列表来实现这些操作的。
观察者(Observer)的注册功能需要调用主题的 registerObserver() 方法。Implementation
天气数据布告板会在天气信息发生改变时更新其内容,布告板有多个,并且在将来会继续增加。```java // 被观察者接口 public interface Subject { // 登记一个新的观察者 void registerObserver(Observer o); // 删除一个登记过的观察者 void removeObserver(Observer o); // 通知所有登记过的观察者对象 void notifyObserver(); }
// 具体被观察者,实现被观察者接口
public class WeatherData implements Subject {
// 观察者列表
private List
public WeatherData() {
observers = new ArrayList<>();
}
public void setMeasurements(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
// 如果更新,则通知它的观察者
notifyObserver();
}
@Override
public void registerObserver(Observer o) {
observers.add(o);
}
@Override
public void removeObserver(Observer o) {
int i = observers.indexOf(o);
if (i >= 0) {
observers.remove(i);
}
}
// 调用观察者的更新方法,完成更新
@Override
public void notifyObserver() {
for (Observer o : observers) {
o.update(temperature, humidity, pressure);
}
}
}
// 观察者接口 public interface Observer { // 更新方法 void update(float temp, float humidity, float pressure); }
// 实现观察者接口 public class StatisticsDisplay implements Observer {
public StatisticsDisplay(Subject weatherData) {
// 将自己注册到被观察者的名单中
weatherData.registerObserver(this);
}
@Override
public void update(float temp, float humidity, float pressure) {
System.out.println("StatisticsDisplay.update: " + temp + " " + humidity + " " + pressure);
}
}
// 实现观察者接口 public class CurrentConditionsDisplay implements Observer {
public CurrentConditionsDisplay(Subject weatherData) {
weatherData.registerObserver(this);
}
@Override
public void update(float temp, float humidity, float pressure) {
System.out.println("CurrentConditionsDisplay.update: " + temp + " " + humidity + " " + pressure);
}
}
public class WeatherStation { public static void main(String[] args) { // 被观察者对象 WeatherData weatherData = new WeatherData(); // 观察者对象1 CurrentConditionsDisplay currentConditionsDisplay = new CurrentConditionsDisplay(weatherData); // 观察者对象2 StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData); // 被观察者更新 weatherData.setMeasurements(0, 0, 0); weatherData.setMeasurements(1, 1, 1); } }
CurrentConditionsDisplay.update: 0.0 0.0 0.0 StatisticsDisplay.update: 0.0 0.0 0.0 CurrentConditionsDisplay.update: 1.0 1.0 1.0 StatisticsDisplay.update: 1.0 1.0 1.0