[设计模式]观察者模式

观察者模式应该是比较好理解的一种设计模式。《Head Frist 设计模式》一书中对观察者模式的解释就是:

出版者+订阅者=观察者模式

适用于需要监听对象变化的场景,拿《Head Frist 设计模式》书中的例子(天气更新)来熟悉一下该设计模式的应用。

天气更新需求:
有三块显示板,需要实时地显示天气数据的变化,三块显示板分别用于显示湿度、温度和气压。

需求分析:
三块显示板分别用于显示湿度、温度和气压,当前天气数据更新(暂时不需要关心天气数据如何更新)时,三块显示板的数据也需要更新。

那么根据需求分析,可以提取如下类关系:


image.png

其中class WeatherData是客户提供的已有类,已知当天气数据变化时,measurementsChanged()会被调用,那么如果需要在天气数据变化时,更新显示面板,那么最简单直接的做法,就是在measurementsChanged()中添加代码去更新三块面板的显示:

    /**
     * 当温度、湿度、压力变化时,下面的方法会被调用
     */
    void measurementsChaged() {
        // to do
        DisplayBoardInterface tempratureDispalyBoard = new TempratureDisplayBoard();
        DisplayBoardInterface humidDispalyBoard = new HumidityDisplayBoard();
        DisplayBoardInterface pressureDispalyBoard = new PressureDisplayBoard();
        
        tempratureDispalyBoard.update(this.temperature);
        humidDispalyBoard.update(this.humidity);
        pressureDispalyBoard.update(this.pressure);
    }

这样做可以满足当前的需求,但是有几个问题:

  • 以后客户可能会变更需求,如需要添加、删除、修改显示板,那么还需要再次修改measurementsChaged(),会再次破坏已有类class WeatherData的封装,这样不符合开闭原则

为了尽量避免上述问题,需要将变化部分和不变部分分离,这种场景下,就可以使用观察者模式来设计:

  • WeatherData出版者
  • TempratureDisplayBoardHumidityDisplayBoardPressureDisplayBoard订阅者

出版者会通知所有 订阅者自身的变化。
那么使用观察者模式如下修改class WeatherData代码:

package com.headfirst.strategy;

import java.util.ArrayList;

public class WeatherData {
    /* 温度 */
    float temperature;
    /* 湿度*/
    float humidity;
    /* 压力 */
    float pressure;
    
    /* 保存所有的观察者 */
    ArrayList<DisplayBoardInterface> observers = new ArrayList<DisplayBoardInterface>();
    
    /* 注册观察者 */
    public void registerObserver(DisplayBoardInterface observer) {
        observers.add(observer);
    }
    
    /* 取消观察者 */
    public void unregisterObserver(DisplayBoardInterface observer) {
        observers.remove(observer);
    }
    
    /**
     * 当温度、湿度、压力变化时,下面的方法会被调用
     */
    void measurementsChaged() {
        // to do
        for (DisplayBoardInterface observer:observers) {
            observer.update(this);
        }
    }
    
    public float getTemprature() {
        return this.temperature;
    }
    
    public float getHumidity() {
        return this.humidity;
    }
    
    public float getPressure() {
        return this.pressure;
    }
    
    public void setTemprature(float value) {
        this.temperature = value;
        measurementsChaged();
    }
    
    public void setHumidity(float value) {
        this.humidity = value;
        measurementsChaged();
    }
    
    public void setPressure(float value) {
        this.pressure = value;
        measurementsChaged();
    }
}

通过注册接口添加观察者(此处只是测试,未考虑取消注册,实际编程时需要注意取消注册,以防泄露):

public class Main {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        //System.out.println("hello world.");
        
        /**
         * 更新天气信息
         */
        WeatherData weatherData = new WeatherData();
        /* 注册观察者 */
        weatherData.registerObserver(new TempratureDisplayBoard());
        weatherData.registerObserver(new HumidityDisplayBoard());
        weatherData.registerObserver(new PressureDisplayBoard());
        
        weatherData.setTemprature(37);
        weatherData.setHumidity(20);
        weatherData.setPressure(1000);
    }

}

这样后续扩展新的观察者,就不需要再次修改class WeatherData的代码。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容