【设计模式——学习笔记】23种设计模式——观察者模式Observer(原理讲解+应用场景介绍+案例介绍+Java代码实现)
文章目录
- 案例引入
- 原始方案实现
- 实现
- 问题分析
- 介绍
- 基础介绍
- 登场角色
- 案例实现
- 案例一
- 类图
- 实现
- 分析
- 案例二
- 类图
- 实现
- 观察者模式在JDK源码的应用
- 总结
- 文章说明
案例引入
有一个天气预报项目,需求如下:
- 气象站可以将每天测量到的温度、湿度、气压等等以公告的形式发布出去(比如发布到自己的网站或第三方)
- 需要设计开放型API,便于其他第三方也能接入气象站获取数据
- 提供温度、气压、湿度的接口
- 测量数据更新时,要能实时的通知给第三方
原始方案实现
设计一个WeatherData类,类里面的方法如下:
- getTemperature0:获取温度
- getHumidity0:获取湿度
- getPressure0:获取气压
- dataChange0:可以定时(如每十分钟调用一次)调用该方法去气象站更新温度、湿度、气压数据,并主动推送给应用者
实现
【当前天气状况】
package com.atguigu.observer;/*** 显示当前天气情况(可以理解成是气象站的网站)* @author Administrator**/
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("***Today mTemperature: " + temperature + "***");System.out.println("***Today mPressure: " + pressure + "***");System.out.println("***Today mHumidity: " + humidity + "***");}
}
【气象台的天气数据】
package com.atguigu.observer;/*** 类是核心* 1. 包含最新的天气情况信息* 2. 含有 CurrentConditions 对象* 3. 当数据有更新时,就主动的调用CurrentConditions对象的update方法(含 display), 这样他们(接入方)就看到最新的信息* @author Administrator**/
public class WeatherData {private float temperatrue;private float pressure;private float humidity;private CurrentConditions currentConditions;/*** 加入新的第三方* @param 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() {//调用 接入方的 updatecurrentConditions.update(getTemperature(), getPressure(), getHumidity());}/*** 当数据有更新时,就调用 setData* @param temperature* @param pressure* @param humidity*/public void setData(float temperature, float pressure, float humidity) {this.temperatrue = temperature;this.pressure = pressure;this.humidity = humidity;//调用dataChange, 将最新的信息 推送给 接入方 currentConditionsdataChange();}
}
【主类】
package com.atguigu.observer;public class Client {public static void main(String[] args) {//创建接入方 currentConditionsCurrentConditions currentConditions = new CurrentConditions();//创建 WeatherData 并将 接入方 currentConditions 传递到 WeatherData中WeatherData weatherData = new WeatherData(currentConditions);//更新天气情况weatherData.setData(30, 150, 40);//天气情况变化System.out.println("============天气情况变化=============");weatherData.setData(40, 160, 20);}
}
【运行】
***Today mTemperature: 30.0***
***Today mPressure: 150.0***
***Today mHumidity: 40.0***
============天气情况变化=============
***Today mTemperature: 40.0***
***Today mPressure: 160.0***
***Today mHumidity: 20.0***Process finished with exit code 0
问题分析
当添加其他第三方网站时,需要修改WeatherData类,不符合开闭原则,不利于维护
改进:推荐使用观察者模式
介绍
基础介绍
- 观察者模式的工作原理:观察者到观察对象中进行注册,当观察对象的状态发生改变时,就通知已经注册的观察者,观察者可以被从观察对象中移除
登场角色
Subject(观察对象)
:Subject角色定义了注册、删除观察者的方法,还可以声明“获取现在的状态”的方法ConcreteSubject(具体的观察对象)
:当自身状态发生变化后,它会通知所有已经注册的Observer角色Observer(观察者)
:负责接收来自Subject角色状态变化的通知,声明了update方法来接收通知ConcreteObserver(具体的观察者)
:当它的update方法被调用后,会去获取要观察的对象的最新状态
案例实现
案例一
类图
实现
【Subject:观察对象接口】
package com.atguigu.observer.improve;/*** 接口, 让WeatherData 来实现*/
public interface Subject {/*** 观察者注册* @param o*/public void registerObserver(Observer o);/*** 移除观察者* @param o*/public void removeObserver(Observer o);/*** 通知所有观察者*/public void notifyObservers();
}
【Observer:观察者接口】
package com.atguigu.observer.improve;/*** 观察者接口*/
public interface Observer {/*** 更新数据** @param temperature* @param pressure* @param humidity*/public void update(float temperature, float pressure, float humidity);
}
【WeatherData:具体观察对象】
package com.atguigu.observer.improve;import java.util.ArrayList;/*** 类是核心* 1. 包含最新的天气情况信息* 2. 含有 观察者集合,使用ArrayList管理* 3. 当数据有更新时,就主动的调用ArrayList, 通知所有的(接入方)就看到最新的信息** @author Administrator*/
public class WeatherData implements Subject {private float temperature;private float pressure;private float humidity;/*** 观察者集合*/private ArrayList<Observer> observers;/*** 构造方法*/public WeatherData() {// 创建新的集合observers = new ArrayList<Observer>();}public float getTemperature() {return temperature;}public float getPressure() {return pressure;}public float getHumidity() {return humidity;}public void dataChange() {//调用 接入方的 updatenotifyObservers();}//当数据有更新时,就调用 setDatapublic void setData(float temperature, float pressure, float humidity) {this.temperature = temperature;this.pressure = pressure;this.humidity = humidity;//调用dataChange, 将最新的信息 推送给 接入方 currentConditionsdataChange();}/*** 注册一个观察者* @param o*/@Overridepublic void registerObserver(Observer o) {observers.add(o);}/*** 移除一个观察者* @param o*/@Overridepublic void removeObserver(Observer o) {if (observers.contains(o)) {observers.remove(o);}}/*** 遍历所有的观察者,并通知*/@Overridepublic void notifyObservers() {for (int i = 0; i < observers.size(); i++) {observers.get(i).update(this.temperature, this.pressure, this.humidity);}}
}
【CurrentConditions:具体观察者】
package com.atguigu.observer.improve;public class CurrentConditions implements Observer {/*** 温度*/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("***Today mTemperature: " + temperature + "***");System.out.println("***Today mPressure: " + pressure + "***");System.out.println("***Today mHumidity: " + humidity + "***");}
}
【BaiduSite:具体观察者】
package com.atguigu.observer.improve;/*** 百度网站*/
public class BaiduSite implements Observer {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("***百度网站 气温 : " + temperature + "***");System.out.println("***百度网站 气压: " + pressure + "***");System.out.println("***百度网站 湿度: " + humidity + "***");}}
【主类】
package com.atguigu.observer.improve;public class Client {public static void main(String[] args) {//创建一个WeatherDataWeatherData weatherData = new WeatherData();//创建观察者CurrentConditions currentConditions = new CurrentConditions();BaiduSite baiduSite = new BaiduSite();//注册到weatherDataweatherData.registerObserver(currentConditions);weatherData.registerObserver(baiduSite);//测试System.out.println("通知各个注册的观察者, 看看信息");weatherData.setData(10f, 100f, 30.3f);System.out.println();System.out.println("----- 移除一个观察者 -----");weatherData.removeObserver(currentConditions);//测试System.out.println();System.out.println("通知各个注册的观察者, 看看信息");weatherData.setData(10f, 100f, 30.3f);}}
【运行】
通知各个注册的观察者, 看看信息
***Today mTemperature: 10.0***
***Today mPressure: 100.0***
***Today mHumidity: 30.3***
===百度网站====
***百度网站 气温 : 10.0***
***百度网站 气压: 100.0***
***百度网站 湿度: 30.3***----- 移除一个观察者 -----通知各个注册的观察者, 看看信息
===百度网站====
***百度网站 气温 : 10.0***
***百度网站 气压: 100.0***
***百度网站 湿度: 30.3***Process finished with exit code 0
分析
- 使用该模式,拓展方便,比如需要新增一个观察者,只需要实现一个具体的观察者类,然后在观察对象中注册即可
案例二
类图
实现
【观察者接口】
package com.atguigu.observer.Sample;public interface Observer {public abstract void update(NumberGenerator generator);
}
【被观察对象抽象类】
package com.atguigu.observer.Sample;import java.util.ArrayList;
import java.util.Iterator;/*** 用来生成数值的抽象类*/
public abstract class NumberGenerator {/*** 保存Observer们*/private ArrayList observers = new ArrayList();/*** 注册Observer** @param observer*/public void addObserver(Observer observer) {observers.add(observer);}/*** 删除Observer** @param observer*/public void deleteObserver(Observer observer) {observers.remove(observer);}/*** 向Observer发送通知*/public void notifyObservers() {Iterator it = observers.iterator();while (it.hasNext()) {Observer o = (Observer) it.next();o.update(this);}}/*** 获取数值** @return*/public abstract int getNumber();/*** 生成数值*/public abstract void execute();
}
【具体的被观察对象】
package com.atguigu.observer.Sample;import java.util.Random;/*** 具体的被观察对象*/
public class RandomNumberGenerator extends NumberGenerator {/*** 随机数生成器*/private Random random = new Random();/*** 当前数值*/private int number;/*** 获取当前数值* @return*/public int getNumber() { return number;}public void execute() {for (int i = 0; i < 20; i++) {// 生成随机数number = random.nextInt(50);// 通知观察者notifyObservers();}}
}
【数值显示观察者】
package com.atguigu.observer.Sample;/*** 使用数字形式显示观察到的数值*/
public class DigitObserver implements Observer {public void update(NumberGenerator generator) {System.out.println("DigitObserver:" + generator.getNumber());try {Thread.sleep(100);} catch (InterruptedException e) {}}
}
【图像显示观察者】
package com.atguigu.observer.Sample;/*** 使用图像显示观察到的数值*/
public class GraphObserver implements Observer {public void update(NumberGenerator generator) {System.out.print("GraphObserver:");int count = generator.getNumber();for (int i = 0; i < count; i++) {System.out.print("*");}System.out.println("");try {Thread.sleep(100);} catch (InterruptedException e) {}}
}
【主类】
package com.atguigu.observer.Sample;public class Main {public static void main(String[] args) {NumberGenerator generator = new RandomNumberGenerator();Observer observer1 = new DigitObserver();Observer observer2 = new GraphObserver();generator.addObserver(observer1);generator.addObserver(observer2);generator.execute();}
}
【运行】
DigitObserver:2
GraphObserver:**
DigitObserver:7
GraphObserver:*******
DigitObserver:6
GraphObserver:******
DigitObserver:26
GraphObserver:**************************
DigitObserver:16
GraphObserver:****************
DigitObserver:6
GraphObserver:******
DigitObserver:28
GraphObserver:****************************
DigitObserver:10
GraphObserver:**********
DigitObserver:6
GraphObserver:******
DigitObserver:10
GraphObserver:**********
DigitObserver:20
GraphObserver:********************
DigitObserver:14
GraphObserver:**************
DigitObserver:21
GraphObserver:*********************
DigitObserver:38
GraphObserver:**************************************
DigitObserver:31
GraphObserver:*******************************
DigitObserver:34
GraphObserver:**********************************
DigitObserver:19
GraphObserver:*******************
DigitObserver:30
GraphObserver:******************************
DigitObserver:0
GraphObserver:
DigitObserver:1
GraphObserver:*Process finished with exit code 0
观察者模式在JDK源码的应用
Observable直接就是类,没有实现Subject接口,直接在类中实现管理Observer的核心方法,当然,可以写更多的子类来集成Observable以便实现更多的操作
总结
- 可替换性强:被观察对象中的观察者集合的观察者类型是Observer角色,可以接收各种各样的具体Observer角色,而且可以统一调用他们的update方法;Observer的update方法接收的数据类型也是Subject,在update方法里面也可以统一调用方法来获取具体被观察对象的属性
- update方法所需要的参数非常灵活,可以自己按照需求来定义
- 观察者模式并非主动去观察,而是被观测对象主动去通知,称为发布——订阅模式可能更加贴切
文章说明
- 本文章为本人学习尚硅谷的学习笔记,文章中大部分内容来源于尚硅谷视频(点击学习尚硅谷相关课程),也有部分内容来自于自己的思考,发布文章是想帮助其他学习的人更方便地整理自己的笔记或者直接通过文章学习相关知识,如有侵权请联系删除,最后对尚硅谷的优质课程表示感谢。
- 本人还同步阅读《图解设计模式》书籍(图解设计模式/(日)结城浩著;杨文轩译–北京:人民邮电出版社,2017.1),进而综合两者的内容,让知识点更加全面
相关文章:

【设计模式——学习笔记】23种设计模式——观察者模式Observer(原理讲解+应用场景介绍+案例介绍+Java代码实现)
文章目录 案例引入原始方案实现实现问题分析 介绍基础介绍登场角色 案例实现案例一类图实现分析 案例二类图实现 观察者模式在JDK源码的应用总结文章说明 案例引入 有一个天气预报项目,需求如下: 气象站可以将每天测量到的温度、湿度、气压等等以公告的…...
【奇葩瑞萨-004】RX系列单片机的GPIO初始化
RX系列单片机的GPIO初始化 与IO口相关的寄存器端口(PORT)寄存器端口功能控制(MPC)寄存器MPC.PmnFPS的设置过程MPC寄存器设置注意事项 端口Pmn的初始化不同端口模式下,PORT、MCP寄存器的配置顺序 感想:与STM…...

【Git】Git切换地址
如何切换git代码地址? 1、查看当前远程 url git remote -v执行命令后,可以看见当前有2个URL。 远程 URL 在一般情况下有两个,分别是 fetch 和 push。 fetch URL 是用于从远程仓库获取最新版本的数据。当您运行 git fetch 命令时…...
elementUI点击当前行更改当前行状态(数据更新DOM不更新问题解决)
<template slot-scope"{row,$index}" slot"menu"><el-button v-if"row.editable" type"text" size"small" click"changeStatus(row,$index)">编辑</el-button><el-button v-else type"…...
python爬取阿里巴巴商品页面数据api
以下是使用Python爬取商品页面的示例代码: import requests from bs4 import BeautifulSoup# 定义要爬取的商品链接 url https://www.alibaba.com/product-detail/High-Quality-Custom-Logo-Printing-Black_60802527914.html# 发送请求 response requests.get(ur…...

angular-mat-select 多选 实现按选择顺序排序
mat-select 正常情况下,多选后,已选项是按列表顺序进行排序,如果我想实现按照点击项目的顺序进行排序,我该如何做呢? [参考网址](Angular order of selected option in multiple mat-select - Stack Overflow) sortComparator是Angular Material中mat-select组件的一个属…...

爬虫010_列表高级_添加_append_extend_修改_查询_in_not int_删除_del_pop_remove---python工作笔记029
然后再来看列表操作 首先添加append方法 然后插入,坐标是要插入的下标,右边是插入的内容 看结果 1,2,3,4,5,6 然后这个extend,是逐个插入,放到后边 然后是修改,直接对下标赋值 看结果</...

微服务服务拆分和远程调用
一、服务架构比较 单体架构:简单方便,高度耦合,扩展性差,适合小型项目。例如:学生管理系统 分布式架构:松耦合,扩展性好,但架构复杂,难度大。适合大型互联网项目&#x…...
MySQL8.1源码安装与部署
官方文档 https://downloads.mysql.com/archives/community/https://dev.mysql.com/doc/refman/8.1/en/binary-installation.html官方文档源码安装步骤 # Preconfiguration setup $> groupadd mysql $> useradd -r -g mysql -s /bin/false mysql # Beginning of source-b…...
algebraic reconstruction technique(ART)
数值线性代数的Kaczmarz方法被Gordon,Bender,Herman引入至CT重建中,称为ART方法。 A x b Axb Axb A A A为 m n m\times n mn的稀疏矩阵。 A A A的元素 a i j a_{ij} aij表示像素 j j j对射线 i i i投影的贡献。 A A A的行向量 a i T a…...

oracle11g安装
oracle11g安装 安装环境 虚拟机版本:centos7.9 虚拟机ip:192.168.5.144 oracle版本:11g oracle安装包:p13390677_112040_Linux-x86-64_1of7.zip,p13390677_112040_Linux-x86-64_2of7.zip,p13390677_11204…...

网络防御(9)
.一、SSL工作过程是什么? SSL位于应用层和传输层之间,它能够为基于TCP等可靠连接的应用层协议提供安全性保证。SSL协议本身分为两层: 上层为SSL握手协议(SSL handshake protocol)、SSLpassword变化协议(S…...
Spring核心与设计思想
文章目录 Spring是什么?认识Spring IoC容器传统的开发图书管理系统设计可能导致的问题 使用IoC容器 Spring是什么? Spring是一个用于构建企业级应用程序的开源框架,它为Java开发者提供了一种简化和加速应用程序开发的方式。Spring框架提供了…...
【stream的使用】使用stream.filter过滤List对象
Stream初相识 概括讲,可以将Stream流操作分为3种类型: 创建Stream Stream中间处理 终止Steam 每个Stream管道操作类型都包含若干API方法,先列举下各个API方法的功能介绍。 开始管道 主要负责新建一个Stream流,或者基于现有的数组…...

Flink多流处理之connect拼接流
Flink中的拼接流connect的使用其实非常简单,就是leftStream.connect(rightStream)的方式,但是有一点我们需要清楚,使用connect后并不是将两个流给串联起来了,而是将左流和右流建立一个联系,作为一个大的流,并且这个大的流可以使用相同的逻辑处理leftStream和rightStream,也可以…...

对任意类型数都可以排序的函数:qsort函数
之前我们学习过冒泡排序: int main() {int arr[] { 9,7,8,6,5,4,3,2,1,0 };int sz sizeof(arr)/sizeof(arr[0]);int i 0;for (i 0; i < sz-1; i) {int j 0;for (j 0; j < sz-1-i; j) {if (arr[j] > arr[j 1]){int temp 0;temp arr[j];arr[j] ar…...
vue数据更新table内容不更新解决方法
场景: table组件绑定的数据变化时,页面没有重新渲染,常见于子组件中使用table组件 原理: 创建实例时 数组在vue中没有被监听到,属于非响应式数据,数组的下标变化无法监听到 解决方式: <e…...
合宙Air724UG LuatOS-Air script lib API--record
record Table of Contents record record.start(seconds, cbFnc, type, quality, rcdType, format, streamRptLen) record.stop(cbFnc) record.getFilePath() record.getData(offset, len) record.getSize() record.delete() record.exists() record.isBusy() record 模块功能&…...

基于Vgg16和Vgg19深度学习网络的步态识别系统matlab仿真
目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 MATLAB2022A 3.部分核心程序 ................................................................ % 设置训练选项options …...

Java分布式微服务3——Docker
文章目录 Docker介绍安装DockerDocker基础操作Docker服务的启动镜像命令容器命令1. 从docker hub去查看Nginx容器的运行命令2. 查看所有容器状态3. 查看容器日志4. 进入Nginx容器执行命令,修改Html内容,添加“Hello World”5. 停止与开始容器6. 删除容器…...
java_网络服务相关_gateway_nacos_feign区别联系
1. spring-cloud-starter-gateway 作用:作为微服务架构的网关,统一入口,处理所有外部请求。 核心能力: 路由转发(基于路径、服务名等)过滤器(鉴权、限流、日志、Header 处理)支持负…...

iPhone密码忘记了办?iPhoneUnlocker,iPhone解锁工具Aiseesoft iPhone Unlocker 高级注册版分享
平时用 iPhone 的时候,难免会碰到解锁的麻烦事。比如密码忘了、人脸识别 / 指纹识别突然不灵,或者买了二手 iPhone 却被原来的 iCloud 账号锁住,这时候就需要靠谱的解锁工具来帮忙了。Aiseesoft iPhone Unlocker 就是专门解决这些问题的软件&…...
【解密LSTM、GRU如何解决传统RNN梯度消失问题】
解密LSTM与GRU:如何让RNN变得更聪明? 在深度学习的世界里,循环神经网络(RNN)以其卓越的序列数据处理能力广泛应用于自然语言处理、时间序列预测等领域。然而,传统RNN存在的一个严重问题——梯度消失&#…...

dedecms 织梦自定义表单留言增加ajax验证码功能
增加ajax功能模块,用户不点击提交按钮,只要输入框失去焦点,就会提前提示验证码是否正确。 一,模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...

学校招生小程序源码介绍
基于ThinkPHPFastAdminUniApp开发的学校招生小程序源码,专为学校招生场景量身打造,功能实用且操作便捷。 从技术架构来看,ThinkPHP提供稳定可靠的后台服务,FastAdmin加速开发流程,UniApp则保障小程序在多端有良好的兼…...

新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案
随着新能源汽车的快速普及,充电桩作为核心配套设施,其安全性与可靠性备受关注。然而,在高温、高负荷运行环境下,充电桩的散热问题与消防安全隐患日益凸显,成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...
DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”
目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...
鸿蒙DevEco Studio HarmonyOS 5跑酷小游戏实现指南
1. 项目概述 本跑酷小游戏基于鸿蒙HarmonyOS 5开发,使用DevEco Studio作为开发工具,采用Java语言实现,包含角色控制、障碍物生成和分数计算系统。 2. 项目结构 /src/main/java/com/example/runner/├── MainAbilitySlice.java // 主界…...
《C++ 模板》
目录 函数模板 类模板 非类型模板参数 模板特化 函数模板特化 类模板的特化 模板,就像一个模具,里面可以将不同类型的材料做成一个形状,其分为函数模板和类模板。 函数模板 函数模板可以简化函数重载的代码。格式:templa…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...