跳转至

观察者模式

分类:行为型模式。

类结构

classDiagram
    direction TB
    class Subject {
        <<abstract>>
        +attach(Observer observer)
        +detach(Observer observer)
        +notify()
    }
    class ConcreteSubject {
        -String state
        +getState() String
        +setState(String state)
    }
    class Observer {
        <<interface>>
        +update()
    }
    class ConcreteObserver {
        +update()
    }
    Subject <|-- ConcreteSubject
    Observer <|.. ConcreteObserver
    Subject o-- "0..*" Observer : 注册并通知
    ConcreteObserver --> ConcreteSubject : 拉取状态

角色定义

角色 定义
Subject(主题) 管理观察者集合,提供注册、移除和通知操作。
ConcreteSubject(具体主题) 保存被观察的状态,在约定的变化时机通知观察者。
Observer(观察者接口) 定义接收主题变化通知的更新操作。
ConcreteObserver(具体观察者) 响应通知并更新自身。图中采用拉取方式,收到 update() 后向具体主题读取状态。

Head First 示例:气象站与布告板

第 2 章中,气象站采集温度、湿度、气压,当前状况、统计等布告板需要随测量值更新。这里依据作者 2020 年第二版仓库中的 observer/weather 实现。

该版本采用**推送数据**:WeatherData 通知时直接把三个测量值传给 Observer.update()。它与本页通用类图展示的“观察者主动拉取状态”是两种通知方式;不要把两套方法签名混用。图中只选两种布告板,省略预测与酷热指数显示。

示例类型 模式角色 职责
Subject 主题接口 定义注册、移除观察者以及发送通知的操作。
WeatherData 具体主题 保存观察者及测量值,在数据改变后推送通知。
Observer 观察者接口 通过带温度、湿度、气压参数的 update() 接收变化。
CurrentConditionsDisplay 具体观察者 保存当前温度与湿度,并显示当前状况。
StatisticsDisplay 具体观察者 累计温度读数,显示平均值、最高值与最低值。
DisplayElement 显示接口 统一布告板的 display() 操作,与订阅接口分离。

示例 UML

classDiagram
    direction TB
    class Subject {
        <<interface>>
        +registerObserver(Observer o)
        +removeObserver(Observer o)
        +notifyObservers()
    }
    class WeatherData {
        +setMeasurements(temperature, humidity, pressure)
        +measurementsChanged()
        +notifyObservers()
    }
    class Observer {
        <<interface>>
        +update(temp, humidity, pressure)
    }
    class DisplayElement {
        <<interface>>
        +display()
    }
    class CurrentConditionsDisplay {
        +update(temperature, humidity, pressure)
        +display()
    }
    class StatisticsDisplay {
        +update(temp, humidity, pressure)
        +display()
    }
    Subject <|.. WeatherData
    WeatherData o-- "0..*" Observer : 注册并推送
    Observer <|.. CurrentConditionsDisplay
    Observer <|.. StatisticsDisplay
    DisplayElement <|.. CurrentConditionsDisplay
    DisplayElement <|.. StatisticsDisplay
    CurrentConditionsDisplay --> WeatherData : 订阅主题
    StatisticsDisplay --> WeatherData : 订阅主题

协作过程

  1. 创建 WeatherData,再把它传入各布告板构造器;布告板调用 registerObserver(this) 注册。
  2. 气象站通过 setMeasurements() 更新三个测量值。
  3. measurementsChanged() 调用 notifyObservers(),逐一执行观察者的 update()。
  4. 各布告板更新自己的数据并调用 display();主题只依赖 Observer 接口。

配套源码(2020 年第二版,固定版本):

评论