bboyjing's blog

HeadFirst设计模式二【观察者模式】

观察者模式的核心是订阅、通知,是个非常有用的设计模式,我们下面就来学习下。

场景描述

书中描述了一个气象数据更新的场景。大概意思是,气象站数据会不定时更新,有好几种布告板需要实时显示,而且必须可扩展,可以随时添加、删除布告板。这种情况下就可以使用到订阅通知模式了。这个场景比较简单,实现起来也不复杂,下面就直接看看代码。

代码示例

  1. 定义主题接口

    1
    2
    3
    4
    5
    6
    7
    public interface Subject {
    public void registerObserver(Observer o);
    public void removeObserver(Observer o);
    public void notifyObserver();
    }
  2. 定义观察者接口,以及显示接口。其中Observer接口是核心,DisplayElement接口是为了指明布告板是需要UI显示出来,因为我们也可以实现一个不需要显示的观察者,抽出单独的接口会更灵活。

    1
    2
    3
    4
    5
    6
    7
    public interface Observer {
    public void update(float temperature, float humidity, float pressure);
    }
    public interface DisplayElement {
    public void display();
    }
  3. 实现主题,该实现的核心就在于WeatherData对象中保存了所有观察者对象,通过调用各个观察者的update方法,来实现数据变更通知。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    public class WeatherData implements Subject {
    public List<Observer> observers;
    private float temperature;
    private float humidity;
    private float pressure;
    public WeatherData() {
    observers = new ArrayList<>();
    }
    /**
    * 注册观察者
    *
    * @param o
    */
    @Override
    public void registerObserver(Observer o) {
    observers.add(o);
    }
    /**
    * 取消订阅
    *
    * @param o
    */
    @Override
    public void removeObserver(Observer o) {
    int i = observers.indexOf(o);
    if (i >= 0) {
    observers.remove(i);
    }
    }
    /**
    * 通知观察者数据变更
    */
    @Override
    public void notifyObserver() {
    observers.forEach(v -> v.update(temperature, humidity, pressure));
    }
    /**
    * 当气象站得到更新观测值时,通知观察者
    */
    public void measurementsChanged() {
    notifyObserver();
    }
    /**
    * 用来模拟数据变更
    *
    * @param temperature
    * @param humidity
    * @param pressure
    */
    public void setMeasurements(float temperature, float humidity, float pressure) {
    this.temperature = temperature;
    this.pressure = pressure;
    this.humidity = humidity;
    measurementsChanged();
    }
    }
  4. 实现用于显示当前数据的布告板

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    public class CurrentConditionsDisplay implements Observer, DisplayElement {
    private float temperature;
    private float humidity;
    private float pressure;
    private Subject weatherData;
    public CurrentConditionsDisplay(Subject weatherData) {
    this.weatherData = weatherData;
    weatherData.registerObserver(this);
    }
    @Override
    public void update(float temperature, float humidity, float pressure) {
    this.temperature = temperature;
    this.humidity = humidity;
    this.pressure = pressure;
    display();
    }
    @Override
    public void display() {
    System.out.println("Current conditions: " + temperature + "F degrees and " + humidity + "% humidity and " + pressure + " pa");
    }
    }
  5. 测试

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public class WeatherStation {
    public static void main(String[] args) {
    WeatherData weatherData = new WeatherData();
    CurrentConditionsDisplay currentConditionsDisplay = new CurrentConditionsDisplay(weatherData);
    weatherData.setMeasurements(80, 65, 30.4f);
    weatherData.setMeasurements(82, 70, 29.2f);
    weatherData.setMeasurements(78, 90, 39.2f);
    }
    }

这个模式看起来好像比较简单,我们看看它的定义:观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会受到通知并自动更新。所以整个核心就在于WeatherData中保存了所有Observer对象。

总结

观察者模式就记录到这儿了,感觉意犹未尽,却又不知如何发力,可能是道行太浅,继续修炼吧。