• 设计模式-观察者模式详解


    气象站建立

    需求:

    1. 气象站必须建立在WeatherData对象上。
    2. 三种布告板,显示目前的气象统计以及简单的预报。
    3. WeatherData对象获得最新的测量数据时,三种布告板必须实时更新。
    4. 可扩展。

    气象监测应用概况

    此系统中的三个部分是:

    1. 气象站(获取实际气象数据的物理装置)。
    2. WeatherData对象(追踪来自气象站的数据,并更新布告板)。
    3. 布告板(显示目前天气状况给用户看)。

    WeatherData对象知道如何跟物理气象站联系,以取得更新的数据。WeatherData对象会随即更新三个布告板的显示:目前状况(温度、湿度、气压)、气象统计和天气预报

    image

    目前已知信息如下:

    1. WeatherData类具有getter方法,可以取得三个测量值:温度、湿度与气压。
    2. 当新的测量数据备妥时,measurementsChanged()方法就会被调用(我们不在乎此方法是如何被调用的,我们只在乎它被调用了)。
    3. 我们需要实现三个使用天气数据的布告板:“目前状况”布告、“气象统计”布告、“天气预报”布告。一旦WeatherData有新的测量,这些布告必须马上更新。
    4. 此系统必须可扩展,其他开发人员可定制布告板。

    错误的示范

    在measurementsChanged()方法中添加我们的代码:

    public class WeatherData{
        public void measurementsChanged(){
            float temp = getTemperature();
            float humidity = getHumidity();
            float pressure = getPressure();
            
            //调用每个布告板更新显示,传入最新的测量
            currentConditionsDisplay.update(temp,humidity,pressure);
            statisticsDisplay.update(temp,humidity,pressure);
            forecastDisplay.update(temp,humidity,pressure);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    我们的实现有什么不对?

    1. 针对具体实现编程,会导致我们以后在增加或删除布告板时必须修改程序。
    2. 改变的地方,需要封装起来。
    3. update方法参数相同,这里更像是一个统一的接口。

    认识观察者模式

    我们先来看看报纸和杂志的订阅是怎么回事:

    1. 报社的业务就是出版报纸。
    2. 向某家报社订阅报纸,只要它们有新报纸出版,就会给你送来,只要你是他们的订户,你就会一直收到新报纸。
    3. 当你不想再看报纸的时候,取消订阅,他们呢就不会再送新报纸来。
    4. 只要报社还在运营,就会一直有人(或单位)向它们订阅报纸或取消订阅报纸。

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

    1. 主题对象管理某些数据。
    2. 当主题内的数据改变,就会通知观察者。
    3. 一旦数据改变,新的数据会以某种形式送到观察者手上。
    4. 观察者已经订阅(注册)主题以便在主题数据改变时能够收到更新。
    5. 不是观察者的对象,不会被通知。

    定义观察者模式

    观察者模式定义了对象之间的一对多依赖,这样以来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。

    • 观察者模式定义了一系列对象之间的一对多关系。
    • 当一个对象改变状态时,其他依赖者都会收到通知。

    image

    当两个对象之间松耦合,它们依然可以交互,但是不太清楚彼此的细节。

    观察者模式提供了一种对象设计,让主题和观察者之间松耦合。

    我们可以独立地复用主题或观察者。如果我们在其他地方需要使用主题或观察者,可以轻易地复用,因为二者并非紧耦合。

    设计气象站

    image

    实现气象站

    /**
     * 主题抽象
     * Created on 2022/9/17
     *
     * @author: jia_xx
     */
    public interface Subject {
        /**
         * 观察者注册
         *
         * @param o
         */
        void registerObserver(Observer o);
    
        /**
         * 观察者删除
         *
         * @param o
         */
        void removeObserver(Observer o);
    
        /**
         * 当主题状态改变时,这个方法会被调用,以通知所有的观察者
         */
        void notifyObservers();
    }
    /**
     * 观察者抽象
     * Created on 2022/9/17
     *
     * @author: jia_xx
     */
    public interface Observer {
        /**
         * 当气象观测值改变时,主题会把这些状态值当作方法的参数,传送给观察者。
         *
         * @param temp
         * @param humidity
         * @param pressure
         */
        void update(float temp, float humidity, float pressure);
    }
    
    /**
     * 布告板显示接口
     * Created on 2022/9/17
     *
     * @author: jia_xx
     */
    public interface DisplayElement {
        /**
         * 当布告板需要显示时,调用此方法
         */
        void display();
    }
    
    • 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
    /**
     * 主题实现
     * @ClassName WeatherData
     * @Author jia_xx
     * @Date 2022/9/17 23:18
     */
    public class WeatherData implements Subject {
        private List observers;
        private float temperature;
        private float humidity;
        private float pressure;
    
        public WeatherData() {
            observers = new ArrayList();
        }
    
        @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 notifyObservers() {
            for (int i = 0; i < observers.size(); i++) {
                Observer observer = (Observer) observers.get(i);
                observer.update(temperature, humidity, pressure);
            }
        }
    
        public void measurementsChanged() {
            notifyObservers();
        }
    
    
        public void setMeasurements(float temperature, float humidity, float pressure) {
            this.temperature = temperature;
            this.humidity = humidity;
            this.pressure = pressure;
        }
    }
    
    • 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
    /**
     * 布告板实现
     * @ClassName CurrentConditionsDisplay
     * @Author jia_xx
     * @Date 2022/9/17 23:19
     */
    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);
        }
    
        @Override
        public void update(float temp, float humidity, float pressure) {
            this.temperature = temp;
            this.humidity = humidity;
            display();
        }
    
        @Override
        public void display() {
            System.out.println("current conditions: " + temperature + "F degrees and " + humidity + "% humidity");
        }
        
    }
    
    • 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
    /**
     * 观察者模式测试类(气象站实现)
     * @ClassName WeatherStation
     * @Author jia_xx
     * @Date 2022/9/17 23:26
     */
    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.measurementsChanged();
        }
    }
    
    output:
    current conditions: 80.0F degrees and 65.0% humidity
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    使用java内置的观察者模式

    /**
     * @ClassName WeatherData
     * @Author jia_xx
     * @Date 2022/9/21 22:28
     */
    public class WeatherData extends Observable {
        private float temperature;
        private float humidity;
        private float pressure;
    
        /**
         * 构造器不再需要为了记住观察者们而建立数据结构了
         */
        public WeatherData(){}
    
        public void measurementsChanged(float temperature, float humidity, float pressure){
            //在拉取之前,状态改变
    //        setChanged();
            //notifyObservers没有传送数据对象,这表示我们采用的做法是“拉”
    //        notifyObservers();
            this.temperature = temperature;
            this.humidity = humidity;
            this.pressure = pressure;
        }
    
        /**
         * 观察者利用这些方法取得WeatherData对象的状态
         * @return
         */
        public float getTemperature(){
            return temperature;
        }
        public float getHumidity(){
            return humidity;
        }
        public float getPressure(){
            return pressure;
        }
    }
    
    • 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
    /**
     * @ClassName CurrentConditionsDisplay
     * @Author jia_xx
     * @Date 2022/9/21 22:51
     */
    public class CurrentConditionsDisplay implements Observer, DisplayElement {
        //主题对象
        Observable observable;
        private float temperature;
        private float humidity;
    
        public CurrentConditionsDisplay(Observable observable){
            this.observable = observable;
            observable.addObserver(this);
        }
        @Override
        public void display() {
            System.out.println("current conditions: "+temperature+"F degrees and "+humidity+"% humidity");
        }
    
        @Override
        public void update(Observable o, Object arg) {
            if(o instanceof WeatherData){
                WeatherData weatherData = (WeatherData)o;
                this.temperature = weatherData.getTemperature();
                this.humidity = weatherData.getHumidity();
                display();
            }
        }
    }
    
    • 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
    /**
     * @ClassName Test
     * @Author jia_xx
     * @Date 2022/9/21 22:45
     */
    public class Test {
        public static void main(String[] args) {
            WeatherData weatherData = new WeatherData();
            CurrentConditionsDisplay currentConditionsDisplay = new CurrentConditionsDisplay(weatherData);
            weatherData.measurementsChanged(80,65,30.4f);
            currentConditionsDisplay.update(weatherData,null);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • Observable是一个类,非接口,限制了Observable的复用潜力。
    • Observable将关键方法保护起来,setChange()被定义成protected。
  • 相关阅读:
    HTTP-HTTPS区别详解
    CenterNet根据自己的数据训练模型
    【Vim】单行与多行缩进
    金仓数据库KingbaseES ksql工具用户指南及参考--4. 使用 Ksql
    如何处理前端本地存储和缓存?
    【消息队列】MQ 八股文必背
    Golang基础学习笔记
    将less、scss编译成css样式的方法
    贝叶斯学习
    Java面试题(六)--Redis
  • 原文地址:https://blog.csdn.net/wozaibohaibian/article/details/126862408