• 设计模式之观察者模式


    笔记来源:尚硅谷Java设计模式(图解+框架源码剖析)

    观察者模式概述

    观察者(Observer)模式中包含两种对象,分别是目标对象和观察者对象。在目标对象和观察者对象间存在着一种一对多的对应关系,当这个目标对象的状态发生变化时,所有依赖于它的观察者对象都会得到通知并执行它们各自特有的行为。

    通俗地说,就好像这些观察者对象在时刻注视着目标对象(被观察)。无论何时该目标对象的状态发生变化,这些观察者对象都能够马上知道,并根据目标对象的新状态执行相应的任务。

    观察者模式又叫发布-订阅(Publish-Subscribe)模式,其中的订阅表示这些观察者对象需要向目标对象进行注册,这样目标对象才知道有哪些对象在观察它。发布指的是当目标对象的状态改变时,它就向它所有的观察者对象发布状态更改的消息,以让这些观察者对象知晓。

    一个目标对象的观察者对象数量是不固定的,可以随时增加新的观察者对象或取消已有的观察者对象。观察者模式的主要优点就是极大地降低了目标对象和观察者对象间的耦合,二者可以独自地改变和复用,让对系统增加功能或删除功能都很方便。

    举例:天气预报

    天气预报项目需求,具体要求如下:

    1. 气象站可以将每天测量到的温度,湿度,气压等等以公告的形式发布出去(比如发布到自己的网站或第三方)。
    2. 需要设计开放型 API,便于其他第三方也能接入气象站获取数据。
    3. 提供温度、气压和湿度的接口
    4. 测量数据更新时,要能实时的通知给第三方

    传统的设计方案

    public class CurrentConditions {
        // 温度,气压,湿度
        private float temperature;
        private float pressure;
        private float humidity;
        //更新 天气情况,是由 WeatherData 来调用,我使用推送模式
        public void update(float temperature, float pressure, float humidity) {
            this.temperature = temperature;
            this.pressure = pressure;
            this.humidity = humidity;
            display();
        }
        //显示
        public void display() {
            System.out.println("***Today mTemperature: " + temperature + "***");
            System.out.println("***Today mPressure: " + pressure + "***");
            System.out.println("***Today mHumidity: " + humidity + "***");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    public class WeatherData {
        private float temperatrue;
        private float pressure;
        private float humidity;
        // 单独声明一个或多个第三方,耦合性强
        private CurrentConditions currentConditions;
        //加入新的第三方
        public WeatherData(CurrentConditions currentConditions) {
            this.currentConditions = currentConditions;
        }
        public float getTemperature() {
            return temperatrue;
        }
        public float getPressure() {
            return pressure;
        }
        public float getHumidity() {
            return humidity;
        }
        public void dataChange() {
            //调用 接入方的 update
            currentConditions.update(getTemperature(), getPressure(), getHumidity());
        }
        //当数据有更新时,就调用 setData
        public void setData(float temperature, float pressure, float humidity) {
            this.temperatrue = temperature;
            this.pressure = pressure;
            this.humidity = humidity;
            //调用 dataChange, 将最新的信息 推送给 接入方 currentConditions
            dataChange();
        }
    }
    
    • 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

    观察者模式设计方案

    Subject:登记注册、移除和通知

    1. registerObserver:注册
    2. removeObserver :移除
    3. notifyObservers:通知所有的注册的用户,根据不同需求,可以是更新数据,让用户来取,也可能是实施推送,看具体需求定

    Observer:接收输入

    观察者模式:对象之间多对一依赖的一种设计方案,被依赖的对象为 Subject,依赖的对象为 Observer,Subject 通知 Observer 变化,比如这里的气象站是 Subject,是 1 的一方。用户时 Observer,是多的一方。本质上,观察者模式是在Subject中增加集合工具(一般是ArrayList)来进行对Observer类的添加、删除、通知的控制,仅此而已。

    类图说明

    image-20220919152320727

    代码实现

    public interface Observer {
        public void update(float temperature, float pressure, float humidity);
    }
    
    • 1
    • 2
    • 3
    public interface Subject {
        public void registerObserver(Observer o);
        public void removeObserver(Observer o);
        public void notifyObservers();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    public class CurrentConditions implements Observer {
        // 温度,气压,湿度
        private float temperature;
        private float pressure;
        private float humidity;
        // 更新 天气情况,是由 WeatherData 来调用,我使用推送模式
        public void update(float temperature, float pressure, float humidity) {
            this.temperature = temperature;
            this.pressure = pressure;
            this.humidity = humidity;
            display();
        }
        // 显示
        public void display() {
            System.out.println("***Today mTemperature: " + temperature + "***");
            System.out.println("***Today mPressure: " + pressure + "***");
            System.out.println("***Today mHumidity: " + humidity + "***");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    public class BaiduSite implements Observer {
        // 温度,气压,湿度
        private float temperature;
        private float pressure;
        private float humidity;
        // 更新 天气情况,是由 WeatherData 来调用,我使用推送模式
        public void update(float temperature, float pressure, float humidity) {
            this.temperature = temperature;
            this.pressure = pressure;
            this.humidity = humidity;
            display();
        }
        // 显示
        public void display() {
            System.out.println("===百度网站====");
            System.out.println("***百度网站 气温 : " + temperature + "***");
            System.out.println("***百度网站 气压: " + pressure + "***");
            System.out.println("***百度网站 湿度: " + humidity + "***");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    public class WeatherData implements Subject {
        private float temperatrue;
        private float pressure;
        private float humidity;
        //观察者集合,通过集合来控制观察者的添加,删除和通知
        private ArrayList<Observer> observers;
        //加入新的第三方
        public WeatherData() {
            observers = new ArrayList<Observer>();
        }
        public float getTemperature() {
            return temperatrue;
        }
        public float getPressure() {
            return pressure;
        }
        public float getHumidity() {
            return humidity;
        }
        public void dataChange() {
            //调用 接入方的 update
            notifyObservers();
        }
        //当数据有更新时,就调用 setData
        public void setData(float temperature, float pressure, float humidity) {
            this.temperatrue = temperature;
            this.pressure = pressure;
            this.humidity = humidity;
            //调用 dataChange, 将最新的信息 推送给 接入方 currentConditions
            dataChange();
        }
        //注册一个观察者
        @Override
        public void registerObserver(Observer o) {
            // TODO Auto-generated method stub
            observers.add(o);
        }
        //移除一个观察者
        @Override
        public void removeObserver(Observer o) {
            // TODO Auto-generated method stub
            if(observers.contains(o)) {
                observers.remove(o);
            }
        }
        //遍历所有的观察者,并通知
        @Override
        public void notifyObservers() {
            // TODO Auto-generated method stub
            for(int i = 0; i < observers.size(); i++) {
                observers.get(i).update(this.temperatrue, this.pressure, this.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
    • 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
    public class Client {
        public static void main(String[] args) {
            //创建一个 WeatherData
            WeatherData weatherData = new WeatherData();
            //创建观察者
            CurrentConditions currentConditions = new CurrentConditions();
            BaiduSite baiduSite = new BaiduSite();
            //注册到 weatherData
            weatherData.registerObserver(currentConditions);
            weatherData.registerObserver(baiduSite);
            //测试
            System.out.println("通知各个注册的观察者, 看看信息");
            weatherData.setData(10f, 100f, 30.3f);
            weatherData.removeObserver(currentConditions);
            //测试
            System.out.println();
            System.out.println("通知各个注册的观察者, 看看信息");
            weatherData.setData(10f, 100f, 30.3f);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    观察者模式的好处

    1. 观察者模式设计后,会以集合的方式来管理用户(Observer),包括注册,移除和通知。
    2. 这样,我们增加观察者(这里可以理解成一个新的公告板),就不需要去修改核心类 WeatherData 不会修改代码,遵守了 ocp 原则。
  • 相关阅读:
    leetcode刷题日志-151反转字符串中的单词
    (1)SpringCloud 整合Python
    荧光标记转铁蛋白-(FITC, cy3, cy5, cy7, 香豆素, 罗丹明)
    LeetCode(力扣)63. 不同路径 IIPython
    golang使用JWX进行认证和加密
    每日一题44:合作过至少三次的演员和导演
    Laravel文档阅读笔记-Custom Authentication Login And Registration Using Laravel 8
    智慧城市的前景:数字孪生技术在智慧城市中的应用前景
    马斯克发布人形机器人进展,它是否“中看不中用”?
    分布式知识整理
  • 原文地址:https://blog.csdn.net/sd_960614/article/details/126935072