设计模式——观察者模式

  导航: 

【黑马Java笔记+踩坑汇总】JavaSE+JavaWeb+SSM+SpringBoot+瑞吉外卖+SpringCloud+黑马旅游+谷粒商城+学成在线+设计模式+牛客面试题

目录

观察者模式

1、天气预报需求

2、天气预报需求方案之普通方案

3、观察者模式介绍

4、观察者模式优化天气预报案例

5、JDK 的Observable类和Observer类


观察者模式

1、天气预报需求

具体要求如下:

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

2、天气预报需求方案之普通方案

WeatherData类

通过对气象站项目的分析,我们可以初步设计出一个天气数据类WeatherData类

  • 1)通过getXxx方法,可以让第三方接入,并得到相关信息
  • 2)当数据有更新时,气象站通过调用dataChange()去更新数据,当第三方再次获取时,就能得到最新数据,当然也可以推送

CurrentConditions(当前的天气情况)可以理解成是我们气象局的网站

核心代码

当前天气状况类,即气象网,用于展示天气数据

/**
 * 当前的天气情况:可以理解成是气象局的网站
 */
public class CurrentConditions {
    private Float temperature;
    private Float pressure;
    private Float humidity;

    /**
     * 更新天气情况,通过推送的方式,由 WeatherData 调用
     *
     * @param temperature
     * @param pressure
     * @param humidity
     */
    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("*** 当前温度:" + this.temperature + " ℃ ***");
        System.out.println("*** 当前气压:" + this.pressure + " kPa ***");
        System.out.println("*** 当前湿度:" + this.humidity + " %RH ***");
    }
}

天气数据类

/**
 * 核心类
 * 1、包含最新的天气信息情况
 * 2、含有 CurrentConditions 对象
 * 3、当数据更新时,主动调用 CurrentConditions 的 update() 方法
 */
public class WeatherData {
    private Float temperature;
    private Float pressure;
    private Float humidity;
    private CurrentConditions conditions;

    /**
     * 传入 CurrentConditions 对象
     *
     * @param conditions
     */
    public WeatherData(CurrentConditions conditions) {
        this.conditions = conditions;
    }

    public Float getTemperature() {
        return temperature;
    }

    public Float getPressure() {
        return pressure;
    }

    public Float getHumidity() {
        return humidity;
    }

    /**
     * 推送天气数据到网站
     */
    public void dataChange() {
        conditions.update(getTemperature(), getPressure(), getHumidity());
    }

    /**
     * 当天气数据发生变化时进行更新
     *
     * @param temperature
     * @param pressure
     * @param humidity
     */
    public void setData(Float temperature, Float pressure, Float humidity) {
        this.temperature = temperature;
        this.pressure = pressure;
        this.humidity = humidity;
        dataChange();
    }
}

客户端调用

// 创建气象网站对象
CurrentConditions currentConditions = new CurrentConditions();
// 创建气象数据对象,并传入气象网站对象
WeatherData weatherData = new WeatherData(currentConditions);
// 天气发生变化时,更新最新的气象数据
weatherData.setData(10f, 150f, 40f);
//weatherData.setData(15f, 130f, 60f);
//weatherData.setData(13f, 160f, 20f);

测试结果

============最新天气============
*** 当前温度:10.0 ℃ ***
*** 当前气压:150.0 kPa ***
*** 当前湿度:40.0 %RH ***
============最新天气============
*** 当前温度:15.0 ℃ ***
*** 当前气压:130.0 kPa ***
*** 当前湿度:60.0 %RH ***
============最新天气============
*** 当前温度:13.0 ℃ ***
*** 当前气压:160.0 kPa ***
*** 当前湿度:20.0 %RH ***

问题分析

  • 1)其他第三方接入气象站获取数据的问题
  • 2)无法在运行时动态的添加第三方(如xx网站)
  • 3)违反OCP原则 => 观察者模式

WeatherData中增加第三方时,都需要创建对应的第三方公台板对象并加入到dataChange()方法中,既不是动态加入,也不利于维护

3、观察者模式介绍

观察者模式是一种行为型设计模式(用于描述对象之间的通信和责任分配,它定义了对象之间一对多的依赖关系,使得当主题对象状态发生改变时,所有依赖于它的观察者对象都能够得到通知并自动更新。该模式的核心是抽象对象与观察者之间的耦合度达到了最小化,从而使系统更加灵活且易于扩展。

实现方法:

主题对象实现主题接口的注册、移除、通知方法,并管理资源和观察者列表;

观察者对象实现观察者接口的更新方法,并管理资源;

主题对象通知方法:遍历观察者列表执行更新方法。

在观察者模式中,主题对象(也称为被观察者)维护一个观察者列表,并提供方法用于添加、删除和通知观察者。当主题状态发生改变时,它会遍历观察者列表并调用每个观察者的更新方法,从而通知它们状态已经改变。

主题接口:有注册、移除和通知功能;

主题实现类:实现主题接口,管理资源和观察者列表;

观察者接口:发起更新资源请求;

观察者实现类:发起更新资源请求、使用资源

优点:

1. 降低了对象之间的耦合度,因为主题对象不需要知道观察者的具体实现,只需要知道观察者实现了一个特定接口即可。

2. 可以动态扩展观察者列表,方便灵活。

3. 实现了对象之间的一对多依赖关系,提高了系统的可维护性和可重用性。遵守了ocp原则(开闭原则:对扩展开放,对修改关闭)。

缺点

1. 当观察者过多时,通知过程需要花费较多的时间,会影响系统的性能。

2. 如果观察者与主题对象之间存在循环依赖,可能会出现死循环

观察者模式在Java中的应用非常广泛,例如Swing中的Listener、Servlet中的Listener、Spring中的事件监听、JDK的Observable等等。

4、观察者模式优化天气预报案例

Subject接口:主体接口,有注册、移除和通知功能;

WeatherData类:主体实现类,实现Subject接口,聚合观察者列表,管理天气信息和观察者列表;

Observer接口:观察者接口,发起更新天气信息请求;

CurrentCondition类:观察者实现类,发起更新天气信息请求和使用天气

主题Subject

/**
 * 主体对象接口,有注册、移除和通知功能;
 */
public interface Subject {
//注册某观察者
    void registerObserver(Observer o);
//移除某观察者
    void removeObserver(Observer o);
//通知
    void notifyObservers();
}
/**
 * 主体对象实现,聚合观察者列表,管理天气信息和观察者列表;
 */
public class WeatherData implements Subject {
    private Float temperature;
    private Float pressure;
    private Float humidity;
    private List<Observer> observerList;

    public WeatherData() {
        observerList = new ArrayList<>();
    }

    public Float getTemperature() {
        return temperature;
    }

    public Float getPressure() {
        return pressure;
    }

    public Float getHumidity() {
        return humidity;
    }

    /**
     * 推送天气数据到网站
     */
    public void dataChange() {
        notifyObservers();
    }

    /**
     * 当天气数据发生变化时进行更新
     *
     * @param temperature
     * @param pressure
     * @param humidity
     */
    public void setData(Float temperature, Float pressure, Float humidity) {
        this.temperature = temperature;
        this.pressure = pressure;
        this.humidity = humidity;
        dataChange();
    }
//注册某观察者
    @Override
    public void registerObserver(Observer o) {
        observerList.add(o);
    }
//移除某观察者
    @Override
    public void removeObserver(Observer o) {
        if(o!= null && observerList.contains(o)) {
            observerList.remove(o);
        }
    }
//通知
    @Override
    public void notifyObservers() {
        for (Observer observer : observerList) {
            observer.update(temperature, pressure, humidity);
        }
    }
}

 观察者对象Observer

/**
 * 观察者接口,发起更新天气信息请求;
 */
public interface Observer {
    void update(Float temperature, Float pressure, Float humidity);
}
/**
 * 观察者实现,发起更新天气信息请求和使用天气
 */
public class CurrentConditions implements Observer {
    private Float temperature;
    private Float pressure;
    private Float humidity;

    @Override
    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("*** 当前温度:" + this.temperature + " ℃ ***");
        System.out.println("*** 当前气压:" + this.pressure + " kPa ***");
        System.out.println("*** 当前湿度:" + this.humidity + " %RH ***");
    }
}

调用测试

// 创建气象网站对象
CurrentConditions currentConditions = new CurrentConditions();
// 创建气象数据对象
WeatherData weatherData = new WeatherData();
// 注册气象网站对象
weatherData.registerObserver(currentConditions);
// 天气发生变化时,更新最新的气象数据
weatherData.setData(10f, 150f, 40f);
//============最新天气============
//*** 当前温度:10.0 ℃ ***
//*** 当前气压:150.0 kPa ***
//*** 当前湿度:40.0 %RH ***

观察者模式的好处

  • 1)观察者模式设计后,会以集合的方式来管理用户Observer,包括注册、移除和通知
  • 2)这样,我们增加观察者(这里可以理解成一个新的公告板),就不需要去修改核心类WeatherData不会修改代码,遵守了ocp原则(开闭原则)

例如,我们新增SinaWebSiteBaiDuWebSite两个三方网站,接口气象局。此时三方只需实现相应接口即可,WeatherData不需要有任何的改变

/**
 * 新增的三方观察者对象——新浪网
 */
public class SinaWebSite implements Observer {
    private Float temperature;
    private Float pressure;
    private Float humidity;

    /**
     * 更新天气情况,通过推送的方式,由 WeatherData 调用
     *
     * @param temperature
     * @param pressure
     * @param humidity
     */
    @Override
    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("*** 新浪网-当前温度:" + this.temperature + " ℃ ***");
        System.out.println("*** 新浪网-当前气压:" + this.pressure + " kPa ***");
        System.out.println("*** 新浪网-当前湿度:" + this.humidity + " %RH ***");
    }
}
/**
 * 新增的三方观察者对象——百度网
 */
public class BaiDuWebSite implements Observer {
    private Float temperature;
    private Float pressure;
    private Float humidity;

    /**
     * 更新天气情况,通过推送的方式,由 WeatherData 调用
     *
     * @param temperature
     * @param pressure
     * @param humidity
     */
    @Override
    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("*** 百度网-当前温度:" + this.temperature + " ℃ ***");
        System.out.println("*** 百度网-当前气压:" + this.pressure + " kPa ***");
        System.out.println("*** 百度网-当前湿度:" + this.humidity + " %RH ***");
    }
}

调用测试

// 新增三方气象网站,只需注册即可
weatherData.registerObserver(new SinaWebSite());
weatherData.registerObserver(new BaiDuWebSite());
// 天气发生变化时,更新最新的气象数据
weatherData.setData(15f, 120f, 80f);
//============最新天气============
//*** 当前温度:15.0 ℃ ***
//*** 当前气压:120.0 kPa ***
//*** 当前湿度:80.0 %RH ***
//============新浪网-最新天气============
//*** 新浪网-当前温度:15.0 ℃ ***
//*** 新浪网-当前气压:120.0 kPa ***
//*** 新浪网-当前湿度:80.0 %RH ***
//============百度网-最新天气============
//*** 百度网-当前温度:15.0 ℃ ***
//*** 百度网-当前气压:120.0 kPa ***
//*** 百度网-当前湿度:80.0 %RH ***

当三方网站不再需要时,只要做相应的移除即可

// 移除气象网站
weatherData.removeObserver(currentConditions);
weatherData.setData(20f, 160f, 30f);
//============新浪网-最新天气============
//*** 新浪网-当前温度:20.0 ℃ ***
//*** 新浪网-当前气压:160.0 kPa ***
//*** 新浪网-当前湿度:30.0 %RH ***
//============百度网-最新天气============
//*** 百度网-当前温度:20.0 ℃ ***
//*** 百度网-当前气压:160.0 kPa ***
//*** 百度网-当前湿度:30.0 %RH ***

5、JDK 的Observable类和Observer类

JDK提供观察者模式基础功能的主题抽象类和观察者接口:

Observable抽象类 

JDK中的Observable抽象类可以作为实现观察者模式的一种工具用于构建主题(被观察者)对象,并且可以将多个观察者对象添加到主题中。当主题发生变化时,通过调用Observable类的notifyObservers()方法,可以通知所有的观察者对象进行更新,从而实现一对多依赖关系。

Observable类的主要作用是简化观察者模式的实现过程,将观察者模式的基础部分已经实现,程序员只需要编写具体的业务逻辑即可。addObserver()、deleteObserver()和notifyObservers()。

但是需要注意的是,JDK中的Observable类并不是非常灵活和易于扩展,它只提供了简单的实现方式。因此,在实际的项目中,我们有时会采用其他的方案来实现观察者模式,例如使用事件模型、Spring框架中的ApplicationEvent等。

总之,JDK中的Observable类可以作为一种工具来支持观察者模式的实现,它简化了观察者模式的编写,提高了代码的可读性和可维护性。但是在实际应用中,我们需要根据实际情况选择最适合的实现方案。

Observer类

Observer即观察者接口,具有update()方法  

public class Observable {
    private boolean changed = false;
    private Vector<Observer> obs = new Vector();

    public Observable() {
    }

    public synchronized void addObserver(Observer var1) {
        if (var1 == null) {
            throw new NullPointerException();
        } else {
            if (!this.obs.contains(var1)) {
                this.obs.addElement(var1);
            }

        }
    }

    public synchronized void deleteObserver(Observer var1) {
        this.obs.removeElement(var1);
    }

    public void notifyObservers() {
        this.notifyObservers((Object)null);
    }
    //...
}

public interface Observer {
    void update(Observable o, Object arg);
}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/17228.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

销售数据分析怎么做?这篇文章说清楚了

如何分析销售数据&#xff1f;分析销售数据有哪些指标&#xff1f;销售数据分析有什么作用&#xff1f; 销售数据是不是得通过数据分析软件啊&#xff1f; 本文将为您解答疑惑—— 一、分析销售数据的指标 从两个层面上来讲&#xff0c;一个是对销售情况的整体把控&#xf…

红黑树理论详解与Java实现

文章目录 基本定义五大性质红黑树和2-3-4树的关系红黑树和2-3-4树各结点对应关系添加结点到红黑树注意事项添加的所有情况 添加导致不平衡叔父节点不是红色节点&#xff08;祖父节点为红色&#xff09;添加不平衡LL/RR添加不平衡LR/RL 叔父节点是红色节点&#xff08;祖父节点为…

破解马赛克有多「容易」?

刷短视频时&#xff0c;估计大家都看过下面这类视频&#xff0c;各家营销号争相曝光「一分钟解码苹果笔刷背后内容」的秘密。换汤不换药&#xff0c;自媒体们戏称其为「破解马赛克」&#xff0c;殊不知让多少不明真相的用户建立起了错误的认知&#xff0c;也让苹果笔刷第 10086…

【面试】嵌入式C语言题目整理

【面试】嵌入式C语言题目整理 描述内存四区。 内存四区分为&#xff1a;代码区、静态区、堆区、栈区 代码区就是用来存放代码的。 静态区用来存放全局变量、静态变量、常量&#xff08;字符串常量、const修饰的全局变量&#xff09;。 堆区中的内存是由程序员自己申请和释放的&…

九、MyBatis动态SQL

文章目录 九、动态SQL9.1 if9.2 where9.3 trim9.4 choose、when、otherwise9.5 foreach9.6 SQL片段 本人其他相关文章链接 九、动态SQL 9.1 if 总结&#xff1a;根据标签中test属性所对应的表达式决定标签中的内容是否需要拼接到SQL中。 User getUserByParamsWithIf(User user…

Packet Tracer - 在思科路由器上配置 AAA 认证

Packet Tracer - 在思科路由器上配置 AAA 认证 拓扑图 地址分配表 设备 接口 IP 地址 子网掩码 默认网关 交换机端口 R1 G0/1 192.168.1.1 255.255.255.0 不适用 S1 F0/1 S0/0/0 (DCE) 10.1.1.2 255.255.255.252 不适用 不适用 R2 G0/0 192.168.2.1 255.2…

(四)Kubernetes - 手动部署(二进制方式安装)

Kubernetes - 手动部署 [ 3 ] 1 部署work node1.1 创建工作目录并拷贝二进制文件1.2 部署kubelet1.2.1 创建配置文件1.2.2 配置文件1.2.3 生成kubelet初次加入集群引导kubeconfig文件1.2.4 systemd管理kubelet1.2.5 启动并设置开机启动1.2.6 允许kubelet证书申请并加入集群 1.3…

JAVA-异常

文章目录 1.异常的体系1.3异常的分类 2.异常的处理2.2异常的抛出throw2.3异常的捕获2.3.1异常声明throws2.3.2 try-catch捕获并处理2.3.3 finally 2.4 异常的处理流程 3.自定义异常类 1.异常的体系 Throwable&#xff1a;是异常体系的顶层类&#xff0c;其派生出两个重要的子类…

人员拥挤检测系统 yolov5

人员拥挤检测系统通过YOLOv5网络模型算法技术&#xff0c;人员拥挤检测系统算法模型对校园/厂区车间/街道等场景的异常的人群聚集&#xff08;出现拥挤情况&#xff09;时&#xff0c;立刻抓拍存档并通知相关人员及时处理。在介绍Yolo算法之前&#xff0c;首先先介绍一下滑动窗…

ES是如何解决高可用

https://www.cnblogs.com/crazymakercircle/p/15433680.html ES是一个分布式全文检索框架&#xff0c;隐藏了复杂的处理机制&#xff0c;核心数据分片机制、集群发现、分片负载均衡请求路由。 ES的高可用架构&#xff0c;总体如下图&#xff1a; 说明&#xff1a;本文会以pdf…

Java 基础入门篇(一)—— Java 概述

文章目录 一、Java 概述二、Java 的产品 JDK2.1 JDK 安装2.2 Java与 Javac 介绍2.3 Java 程序的开发步骤 三、Java 程序的执行原理四、JDK 的组成五、Java 的跨平台工作原理 一、Java 概述 Java 是 sun 公司在 1995 年推出的一门计算机高级编程语言&#xff0c;其语言风格接近人…

深度学习卷积神经网络学习小结2

简介 经过大约两周左右的学习&#xff0c;对深度学习有了一个初步的了解&#xff0c;最近的任务主要是精读深度学习方向的文献&#xff0c;由于搭建caffe平台失败而且比较耗费时间就没有再尝试&#xff0c;所以并没有做实践方面的工作&#xff0c;本文只介绍了阅读文献学到的知…

外卖项目优化-02-mysql主从复制、读写分离(shardingJdbc)、Nginx(反向代理,负载均衡)

文章目录 瑞吉外卖项目优化-Day02课程内容前言1. MySQL主从复制1.1 介绍1.2 搭建1.2.1 准备工作1.2.2 主库配置1.2.3 从库配置 1.3 测试 2. 读写分离案例 (shardingJdbc)2.1 背景介绍2.2 ShardingJDBC介绍2.3 数据库环境2.4 初始工程导入2.5 读写分离配置2.6 测试 3. 项目实现读…

基于ATECLOUD的航电系统可灵活扩展自动化测试平台

随着电子技术的发展&#xff0c;航电系统在飞机整机中的重要性飞速提升。据统计&#xff0c;近年来航电系统在飞机出厂成本中的比例直线上升&#xff0c;航电系统研发成本已占飞机研制总成本的近30%&#xff0c;并保持着持续扩大的趋势。测试保障作为航电产业链至关重要的一环&…

基于JavaWeb实现的寻码网文章资讯管理系统

一、技术结构 前端&#xff1a;html ajax 后端&#xff1a;SpringBootMybatis-plus 环境&#xff1a;JDK1.8 | Mysql | Maven | Redis 二、功能简介 数据库与代码截图 后端管理-登录页 后端管理-首页 后端管理-文章管理-发布文章 后端管理-文章管理-文章列表 后端管理-文…

【iOS KVO(下) KVO的内部结构和源码】

前言 学习KVO的过程&#xff0c;我分为了KVO的实现过程分析和内部结构的学习&#xff0c;学习了实现过程&#xff0c;接下来看KVO是通过何种内部结构实现如此通知&#x1f4e2;和监听。 1 KVO的存储结构 KVO的实现过程离不开合理的存储结构&#xff0c;用到了如下几个类 GS…

智能安防系统-视频监控系统

一、智能安防系统 1、智能安防系统介绍 安全防范系统成为了智慧城市与物联网行业应用中的一个非常重要的子系统。 安防系统主要包括&#xff1a;视频监控系统、入侵报警系统、出入口控制系统、电子巡查系统以及智能停车场管理系统等5个子系统。 AI人工智能安防系统功能&#xf…

Java8中DateTimeFormatter真的是线程安全的吗?

文章目录 [toc] 1.背景2.解决办法2.1办法一&#xff1a;换姿势或者升级JDK的版本2.1办法二&#xff1a;更换文件名称字生成策略 Java8中DateTimeFormatter真的是线程安全的吗&#xff1f; 答案是否定的 1.背景 由于之前写了一个旷世的ocr的服务,接入了旷世的FaceID的人脸比对…

C++笔记——第十六篇 异常

目录 1.C语言传统的处理错误的方式 2. C异常概念 3. 异常的使用 3.1 异常的抛出和捕获 在函数调用链中异常栈展开匹配原则 3.2异常安全 4.异常的优缺点 1.C语言传统的处理错误的方式 传统的错误处理机制&#xff1a; 1. 终止程序&#xff0c;如assert&#xff0c;缺陷&a…

04-Vue技术栈之组件化编程

目录 1、模块与组件、模块化与组件化1.1 模块1.2 组件1.3 模块化1.4 组件化1.5 传统方式编写应用1.6 组件方式编写应用 2、非单文件组件2.1 基本使用2.2 几个注意点2.3 组件的嵌套2.4 VueComponent2.5 一个重要的内置关系2.6 总结 3、单文件组件3.1 一个.vue 文件的组成(3 个部…