观察者模式

对象行为型模式

模式动机

在软件系统中,可能有一些事件驱动的设计。比如在一个气象系统中,有一个Weather对象,可以获取天气状况;该系统希望设计几种公告板,显示不同的天气状况,公告板需要实时更新,即随着Weather对象的天气属性更新而更新。这时候就需要利用观察者模式来监听数据的更新,实时更新公告板。同时要考虑到扩展性,随时增加新的公告板 / 让开发者自己设计。

观察者模式期望建立一种对象与对象之间的依赖关系,一个对象发生改变时自动通知其他对象。发生改变的对象称为观察目标,被通知的对象称为观察者一个观察目标可以对应多个观察者,而观察者之间没有联系,可以按需添加或删除观察者,增加系统可扩展性。

分析

  • 观察者模式的核心是如何建立对象与对象间的依赖关系
  • 关键对象是观察目标和观察者,一个目标可以有任意数目与之依赖的观察者,一旦目标状态改变,所有观察者都被通知

作为对通知的响应,每个观察者及时更新自己的状态以合目标的状态同步,因此观察者模式也叫发布-订阅模式

松耦合

松耦合指两个对象彼此之间存在交互,但是不清楚彼此的实现细节(即面向对方暴露的接口编程),只要松耦合双方的接口仍被遵守,即使改变任意一方的实现也不会影响另一方,即可以自由地改变。

观察者模式的观察目标和观察者之间是松耦合关系。

实例:WeatherData

部分典型代码如下:

public interface Subject {
public void registerObserver(Observer o);
public void removeObserver(Observer o);
public void notifyObservers();
}

public interface Observer {
public void update(float temp, float humidity, float pressure);
}

public interface DisplayElement {
public void display();
}

public class CurrentConditionsDisplay implements Observer, DisplayElement {
private float temperature;
private float humidity;
private Subject weatherData;

public CurrentConditionsDisplay(Subject weatherData) {
this.weatherData = weatherData;
weatherData.registerObserver(this);
}

public void update(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
display();
}

public void display() {
System.out.println("Current conditions: " + temperature
+ "F degrees and " + humidity + "% humidity");
}
}

优点

  1. 可以实现表示层和数据逻辑层的分离,且定义稳定的消息更新传递机制,抽象了更新接口,各种表示层都可以作为观察者(实现抽象观察者的update即可)
  2. 观察目标和观察者之间是松耦合(抽象耦合,只耦合接口)
  3. 支持广播
  4. 符合开闭原则

缺点

  1. 如果观察目标有很多直接或间接观察者,那么通知到所有观察者会耗费很多时间
  2. 如果观察者和观察目标之间不小心引入了循环依赖,那么观察目标会触发循环调用,导致系统崩溃
  3. 观察者永远无法知道观察目标是怎么变化的,只知道变了

适用场合

  • 一个对象的改变会导致其他对象的改变,但不知道具体有多少对象改变,使用观察者模式可以降低这些对象之间的耦合度
  • 一个对象必须通知其他对象,但并不知道这些对象是谁

模式扩展

Java观察者模式

Java提供对观察者模式的支持,在java,util包中提供了Observable类和Observer接口以支持观察者模式

setChanged() {
changed = true
}
notifyObservers (Object arg) {
if (changed) {
for every observer on the list {
call update (this, arg)
}
changed = false
}
}
notifyObservers() {
notifyObservers(null)
}

通知机制——谁来触发更新?

目标和观察者依赖通知机制来保持一致,但是谁来调用notify触发更新呢?有下面两个选择,各有利弊:

  • 目标对象的状态设定操作在改变目标状态后自动调用notify,优点是客户端没有记忆负担,不需要记住在目标对象上调用notify;缺点是连续set操作会触发多次更新,效率低
  • 客户负责在适当的时候调用notify,优点是可以在一系列set之后一次性触发更新,批处理的效率更高;缺点是客户可能忘记,出错率较高

因此当目标和观察者之间的依赖关系比较复杂时,需要引入一个专门维护依赖关系的对象,称为ChangeManager

其目标是观察目标状态变化触发观察者更新所需的工作量。比如如果一个更新操作涉及几个相互依赖的观察目标都变化,那么cm会保证仅在所有目标都更新完毕后才通知它们的观察者,而不是每个目标都通知。

ChangeManager

MVC模式

MVC 模式是一种架构模式,它包含三个角色: 模型(Model),视图 (View)和控制器 (Controller)。观察者模式可以用来实现 MVC 模式,观察者模式中的观察目标就是 MVC 模式中的 模型 (Model),而观察者就是 MVC 中的视图 (View)控制器 (Controller)充当两者之间的中介者(Mediator)。当模型层的数据发生改变时,视图层将自动改变其显示内容

(•‿•)