当前位置: 首页 > news >正文

java设计模式-观察者模式

什么是观察者模式

观察者模式(Observer)是软件设计中的一种行为模式。

它定义了对象之间的一对多关系,其中如果一个对象改变了状态,所有依赖它的对象都会自动被通知并更新。

这种模式包含了两种主要的角色,即被观察者(Subject)和观察者(Observer)。

被观察者维护了一个观察者列表,并提供了注册和删除观察者的方法,当其状态发生变化时,会遍历观察者列表,通知所有观察者。

观察者则定义了一个更新接口,用于接收被观察者的通知并进行相应的更新操作。

这种模式能够使得对象之间的耦合度降低,同时也能够提高系统的灵活性和扩展性。

简单地说,观察者模式定义了一个一对多的依赖关系,让一个或多个观察者对象监听一个主题对象。这样一来,当被观察者状态发生改变时,需要通知相应的观察者,使这些观察者对象能够自动更新。

应用场景

一个对象需要将自己的状态改变通知给其它多个对象,一个对象与它的多个依赖对象需要解耦,以便能对其修改,但不会对其它对象产生影响。

类图和角色

在这里插入图片描述

  • Subject:抽象主题(抽象被观察者),抽象主题角色把所有观察者对象保存在一个集合里,每个主题都可以有任意数量的观察者,抽象主题提供接口,可以增加和删除观察者对象。
  • ConcreteSubject:具体主题(具体被观察者),维护对所有具体观察者的引用的列表,在具体主题的内部状态发生改变时,给所有注册过的观察者发送通知。
  • Observer:抽象观察者,是观察者的抽象类,它定义了一个更新接口,在被观察对象状态改变时会被调用。
  • ConcreteObserver:具体观察者,实现抽象观察者定义的更新接口,以便在得到主题更改通知时更新自身的状态。

代码示例

模拟一个消息订阅推送的例子,有一个微信公众号服务,不定时发布一些消息,关注公众号就可以收到推送消息,取消关注就收不到推送消息。

首先定义抽象主题(抽象被观察者)接口

/*** 抽象主题(抽象被观察者)接口** @author jiangkd* @date 2023/7/28 9:00:10*/
public interface ISubject {/*** 注册一个观察着** @param observer 观察者*/void registerObserver(IObserver observer);/*** 移除一个观察者** @param observer 观察者*/void removeObserver(IObserver observer);/*** 通知所有的观察着*/void notifyObservers();}

然后定义我们的抽象观察者接口

/*** 抽象观察者接口, 所有的观察者需要实现此接口** @author jiangkd* @date 2023/7/28 9:01:36*/
public interface IObserver {/*** 观察者对象接收到通知后的逻辑处理** @param msg 接收消息*/void update(String msg);}

接下来是具体主题(具体被观察者),也就是具体主题(被观察者)接口的实现类,实现了subject接口,对接口中的三个方法进行了实现,同时有一个List集合,用以保存注册的观察者,等需要通知观察者时,遍历该集合即可。

/*** 具体主题(具体被观察者)** @author jiangkd* @date 2023/7/28 10:00:57*/
@Component
public class WeChatMessage implements ISubject {private List<IObserver> observerList = new ArrayList<>();private String message;@Overridepublic void registerObserver(IObserver observer) {observerList.add(observer);}@Overridepublic void removeObserver(IObserver observer) {if (CollUtil.isNotEmpty(observerList)) {observerList.remove(observer);}}@Overridepublic void notifyObservers() {for (IObserver iObserver : observerList) {iObserver.update(message);}}/*** 模拟被观察者的主题更新, 通知所有观察者** @param message 主题更新*/public void updateMessage(String message) {this.message = message;// 通知所有观察者notifyObservers();}}

继续定义具体的观察者,这里我们模拟两个用户进行了订阅主题

/*** 具体的观察者1** @author jiangkd* @date 2023/7/28 10:07:34*/
@Slf4j
@Component
public class User1 implements IObserver {/*** 观察者接收消息, 知道被观察者发生了变化, 自己进行相应的处理, 这里只是测试打印日志而已** @param msg 接收消息*/@Overridepublic void update(String msg) {log.info("我是具体的观察者之一:{}", this.getClass().getSimpleName());log.info("被观察者发生变化, 接收消息:{}", msg);}}
/*** 具体的观察者2** @author jiangkd* @date 2023/7/28 10:09:22*/
@Slf4j
@Component
public class User2 implements IObserver {/*** 观察者接收消息, 知道被观察者发生了变化, 自己进行相应的处理, 这里只是测试打印日志而已** @param msg 接收消息*/@Overridepublic void update(String msg) {log.info("我是具体的观察者之一:{}", this.getClass().getSimpleName());log.info("被观察者发生变化, 接收消息:{}", msg);}}

最后测试一下

/*** @author jiangkd* @date 2023/7/28 10:10:34*/
@SpringBootTest(classes = DemoApplication.class)
@RunWith(SpringRunner.class)
@Slf4j
public class ObserverTest {@ResourceWeChatMessage weChatMessage;@ResourceUser1 user1;@ResourceUser2 user2;@Testpublic void test(){// 绑定被观察者和观察者weChatMessage.registerObserver(user1);weChatMessage.registerObserver(user2);// 模拟被观察者发生变化weChatMessage.updateMessage("今天推送什么呢?");}}

执行结果如下:

2023-07-28 10:15:58.103 INFO 12236 — [ main] d.b.example.design_patterns.gczms.User1 : 我是具体的观察者之一:User1
2023-07-28 10:15:58.104 INFO 12236 — [ main] d.b.example.design_patterns.gczms.User1 : 被观察者发生变化, 接收消息:今天推送什么呢?
2023-07-28 10:15:58.104 INFO 12236 — [ main] d.b.example.design_patterns.gczms.User2 : 我是具体的观察者之一:User2
2023-07-28 10:15:58.104 INFO 12236 — [ main] d.b.example.design_patterns.gczms.User2 : 被观察者发生变化, 接收消息:今天推送什么呢?

记下来我们测试注销一个观察者,首先我们先再次添加一个观察者User3进行订阅主题

/*** 具体的观察者3** @author jiangkd* @date 2023/7/28 10:18:46*/
@Slf4j
@Component
public class User3 implements IObserver {/*** 观察者接收消息, 知道被观察者发生了变化, 自己进行相应的处理, 这里只是测试打印日志而已** @param msg 接收消息*/@Overridepublic void update(String msg) {log.info("我是具体的观察者之一:{}", this.getClass().getSimpleName());log.info("被观察者发生变化, 接收消息:{}", msg);}}

然后测试被观察者发生变化通知到三个订阅者,接着取消User2,只通知User1和User3

/*** @author jiangkd* @date 2023/7/28 10:10:34*/
@SpringBootTest(classes = DemoApplication.class)
@RunWith(SpringRunner.class)
@Slf4j
public class ObserverTest {@ResourceWeChatMessage weChatMessage;@ResourceUser1 user1;@ResourceUser2 user2;@ResourceUser3 user3;@Testpublic void test2(){// 绑定被观察者和观察者weChatMessage.registerObserver(user1);weChatMessage.registerObserver(user2);weChatMessage.registerObserver(user3);// 模拟被观察者发生变化weChatMessage.updateMessage("今天推送什么呢?");log.info("================================================");// 注销其中一个观察者weChatMessage.removeObserver(user2);// 模拟被观察者发生变化weChatMessage.updateMessage("今天天气不错呢!");}}

运行结果日志记录如下

2023-07-28 10:23:21.663 INFO 34500 — [ main] d.b.example.design_patterns.gczms.User1 : 我是具体的观察者之一:User1
2023-07-28 10:23:21.664 INFO 34500 — [ main] d.b.example.design_patterns.gczms.User1 : 被观察者发生变化, 接收消息:今天推送什么呢?
2023-07-28 10:23:21.664 INFO 34500 — [ main] d.b.example.design_patterns.gczms.User2 : 我是具体的观察者之一:User2
2023-07-28 10:23:21.664 INFO 34500 — [ main] d.b.example.design_patterns.gczms.User2 : 被观察者发生变化, 接收消息:今天推送什么呢?
2023-07-28 10:23:21.664 INFO 34500 — [ main] d.b.example.design_patterns.gczms.User3 : 我是具体的观察者之一:User3
2023-07-28 10:23:21.664 INFO 34500 — [ main] d.b.example.design_patterns.gczms.User3 : 被观察者发生变化, 接收消息:今天推送什么呢?
2023-07-28 10:23:21.664 INFO 34500 — [ main] d.b.e.d.gczms.ObserverTest : ================================================
2023-07-28 10:23:21.669 INFO 34500 — [ main] d.b.example.design_patterns.gczms.User1 : 我是具体的观察者之一:User1
2023-07-28 10:23:21.671 INFO 34500 — [ main] d.b.example.design_patterns.gczms.User1 : 被观察者发生变化, 接收消息:今天天气不错呢!
2023-07-28 10:23:21.671 INFO 34500 — [ main] d.b.example.design_patterns.gczms.User3 : 我是具体的观察者之一:User3
2023-07-28 10:23:21.671 INFO 34500 — [ main] d.b.example.design_patterns.gczms.User3 : 被观察者发生变化, 接收消息:今天天气不错呢!

相关文章:

java设计模式-观察者模式

什么是观察者模式 观察者模式&#xff08;Observer&#xff09;是软件设计中的一种行为模式。 它定义了对象之间的一对多关系&#xff0c;其中如果一个对象改变了状态&#xff0c;所有依赖它的对象都会自动被通知并更新。 这种模式包含了两种主要的角色&#xff0c;即被观察…...

HiveSQL SparkSQL中常用知识点记录

目录 0. 相关文章链接 1. hive中多表full join主键重复问题 2. Hive中选出最新一个分区中新增和变化的数据 3. Hive中使用sort_array函数解决collet_list列表排序混乱问题 4. SQL中对小数位数很多的数值转换成文本的时候不使用科学计数法 5. HiveSQL & SparkSQL中炸裂…...

mac不识别移动硬盘导致无法拷贝资源

背景 硬盘插入到Mac电脑上之后&#xff0c;mac不识别移动硬盘导致无法拷贝资源。 移动硬盘在Mac上无法被识别的原因可能有很多&#xff0c;多数情况下&#xff0c;是硬盘的格式与Mac电脑不兼容。 文件系统格式不兼容 macOS使用的文件系统是HFS或APFS&#xff0c;如果移动硬盘是…...

Opencv的Mat内容学习

来源&#xff1a;Opencv的Mat内容小记 - 知乎 (zhihu.com) 1.Mat是一种图像容器&#xff0c;是二维向量。 灰度图的Mat一般存放<uchar>类型 RGB彩色图像一般存放<Vec3b>类型。 (1)单通道灰度图数据存放样式&#xff1a; (2)RGB三通道彩色图存放形式不同&#x…...

MySQL~数据库的设计

二、数据库的设计 1、多表之间的关系 1.1 三种分类 一对一&#xff1a; 分析&#xff1a;一个人只有一个身份证&#xff0c;一个身份证只能对应一个人 如&#xff1a;人和身份证 一对多&#xff1a; 如&#xff1a;部门和员工 分析&#xff1a;一个部门有多个员工&#xff…...

开源了!最强原创图解八股文面试网来袭

强烈推荐 Github上业内新晋的一匹黑马—Java图解八股文面试网—Java2Top.cn&#xff0c;图解 Java 大厂面试题&#xff0c;深入全面&#xff0c;真的强烈推荐~ 这是一个二本逆袭阿里的大佬根据自己秋招上岸所看过的相关专栏&#xff0c;面经&#xff0c;课程&#xff0c;结合自…...

微信小程序开发6

一、分包-基础概念 1.1、什么是分包 分包指的是把一个完整的小程序项目&#xff0c;按照需求划分为不同的子包&#xff0c;在构建时打包成不同的分包&#xff0c;用户在使用时按需进行加载。 1.2、分包的好处 对小程序进行分包的好处主要有以下两点&#xff1a; 可以优化小程序…...

JS 根据身份证号获取年龄、性别、出生日期

先说一代身份证和二代身份证的区别: 1.编号位数不同&#xff0c;第一代身份证为15位号码&#xff0c;第二代证是18位号码 2.编码规则不同&#xff0c;第一代身份证在前6位号码后没有完整出生年份&#xff0c;而二代的有完整的出生年份&#xff0c;一代身份证将年份前二位省略…...

Python+Mongo+LSTM(GTP生成)

下面是一个简单的示例来展示如何使用Python和MongoDB来生成LSTM预测算法。 首先&#xff0c;我们需要安装pymongo和tensorflow库&#xff0c;可以使用以下命令进行安装&#xff1a; pip install pymongo tensorflow接下来&#xff0c;我们连接到MongoDB数据库并获取需要进行预…...

关于idea如何成功运行web项目

导入项目 如图 依次选择 file - new - Project from Existing Sources 选择存放的项目目录地址 如图 导入完成 点击ok 如图 依次选择 Create project from existing sources 点击next如图 &#xff0c;此处默认即可 点击 next如图 点击next有该提示 是因为之前导入过…...

python读取json文件

import json# 文件路径(同目录文件名即可,不同目录需要绝对路径) path 1.json# 读取JSON文件 with open(path, r, encodingutf-8) as file:data json.load(file)#data为字典 print(data) print(type(data))...

迁移学习、微调、计算机视觉理论(第十一次组会ppt)

@TOC 数据增广 迁移学习 微调 目标检测和边界框 区域卷积神经网络R—CNN...

特殊矩阵的压缩存储

1 数组的存储结构 1.1 一维数组 各数组元素大小相同&#xff0c;且物理上连续存放。第i个元素的地址位置是&#xff1a;a[i] LOC i*sizeof(ElemType) (LOC为起始地址) 1.2 二维数组 对于多维数组有行优先、列优先的存储方法 行优先&#xff1a;先行后列&#xff0c;先存储…...

【网络原理】 (1) (应用层 传输层 UDP协议 TCP协议 TCP协议段格式 TCP内部工作机制 确认应答 超时重传 连接管理)

文章目录 应用层传输层UDP协议TCP协议TCP协议段格式TCP内部工作机制确认应答超时重传 网络原理部分我们主要学习TCP/IP协议栈这里的关键协议(TCP 和 IP),按照四层分别介绍.(物理层,我们不涉及). 应用层 我们需要学会自定义一个应用层协议. 自定义协议的原因? 当前的软件(应用…...

【SQL语句】

目录 一、SQL语句类型 1.DDL 2.DML 3.DLL 4.DQL 二、数据库操作 1.查看 2.创建 2.1 默认字符集 2.2 指定字符集 3.进入 4.删除 5.更改 5.1 库名称 5.2 字符集 三、数据表操作 1.数据类型 1.1 数值类型&#xff08;常见&#xff0c;下同&#xff09; 1.1.1 T…...

自动驾驶和机器人学习和总结专栏汇总

汇总如下&#xff1a; 一. 器件选型心得&#xff08;系统设计&#xff09;--1_goldqiu的博客-CSDN博客 一. 器件选型心得&#xff08;系统设计&#xff09;--2_goldqiu的博客-CSDN博客 二. 多传感器时间同步方案&#xff08;时序闭环&#xff09;--1 三. 多传感器标定方案&…...

【C++初阶】C++基础(下)——引用、内联函数、auto关键字、基于范围的for循环、指针空值nullptr

目录 1. 引用 1.1 引用概念 1.2 引用特性 1.3 常引用 1.4 使用场景 1.5 传值、传引用效率比较 1.6 引用和指针的区别 2. 内联函数 2.1 概念 2.2 特性 3.auto关键字&#xff08;C11&#xff09; 3.1 类型别名思考 3.2 auto简介 3.3 auto的使用细则 3.4 auto不能推…...

OSI 7层模型 TCPIP四层模型

》Ref&#xff1a; 1. 这个写的嘎嘎好&#xff0c;解释了为啥4层7层5层&#xff0c;还有数据包封装的问题:数据包在网络中的传输过程详解_数据包传输_张孟浩_jay的博客-CSDN博客 2. HTTP协议 与 TCP协议 的区别&#xff0c;作为web程序员必须要懂 - 知乎 (zhihu.com) 3. 数据…...

iOS-持久化

目的 1.快速展示&#xff0c;提升体验 已经加载过的数据&#xff0c;用户下次查看时&#xff0c;不需要再次从网络&#xff08;磁盘&#xff09;加载&#xff0c;直接展示给用户 2.节省用户流量&#xff08;节省服务器资源&#xff09; 对于较大的资源数据进行缓存&#xf…...

PC音频框架学习

1.整体链路 下行播放&#xff1a; App下发音源→CPU Audio Engine 信号处理→DSP数字信号处理→Codec DAC→PA→SPK 上行录音&#xff1a; MIC拾音→集成运放→Codec ADC→DSP数字信号处理→CPU Audio Engine 信号处理→App 2.硬件 CPU PCH DSP(可选) Codec PA SPKbox MIC…...

Java 8 Stream API 入门到实践详解

一、告别 for 循环&#xff01; 传统痛点&#xff1a; Java 8 之前&#xff0c;集合操作离不开冗长的 for 循环和匿名类。例如&#xff0c;过滤列表中的偶数&#xff1a; List<Integer> list Arrays.asList(1, 2, 3, 4, 5); List<Integer> evens new ArrayList…...

相机Camera日志实例分析之二:相机Camx【专业模式开启直方图拍照】单帧流程日志详解

【关注我&#xff0c;后续持续新增专题博文&#xff0c;谢谢&#xff01;&#xff01;&#xff01;】 上一篇我们讲了&#xff1a; 这一篇我们开始讲&#xff1a; 目录 一、场景操作步骤 二、日志基础关键字分级如下 三、场景日志如下&#xff1a; 一、场景操作步骤 操作步…...

无法与IP建立连接,未能下载VSCode服务器

如题&#xff0c;在远程连接服务器的时候突然遇到了这个提示。 查阅了一圈&#xff0c;发现是VSCode版本自动更新惹的祸&#xff01;&#xff01;&#xff01; 在VSCode的帮助->关于这里发现前几天VSCode自动更新了&#xff0c;我的版本号变成了1.100.3 才导致了远程连接出…...

uniapp微信小程序视频实时流+pc端预览方案

方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度​WebSocket图片帧​定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐​RTMP推流​TRTC/即构SDK推流❌ 付费方案 &#xff08;部分有免费额度&#x…...

C# SqlSugar:依赖注入与仓储模式实践

C# SqlSugar&#xff1a;依赖注入与仓储模式实践 在 C# 的应用开发中&#xff0c;数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护&#xff0c;许多开发者会选择成熟的 ORM&#xff08;对象关系映射&#xff09;框架&#xff0c;SqlSugar 就是其中备受…...

Java线上CPU飙高问题排查全指南

一、引言 在Java应用的线上运行环境中&#xff0c;CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时&#xff0c;通常会导致应用响应缓慢&#xff0c;甚至服务不可用&#xff0c;严重影响用户体验和业务运行。因此&#xff0c;掌握一套科学有效的CPU飙高问题排查方法&…...

Reasoning over Uncertain Text by Generative Large Language Models

https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829 1. 概述 文本中的不确定性在许多语境中传达,从日常对话到特定领域的文档(例如医学文档)(Heritage 2013;Landmark、Gulbrandsen 和 Svenevei…...

tomcat入门

1 tomcat 是什么 apache开发的web服务器可以为java web程序提供运行环境tomcat是一款高效&#xff0c;稳定&#xff0c;易于使用的web服务器tomcathttp服务器Servlet服务器 2 tomcat 目录介绍 -bin #存放tomcat的脚本 -conf #存放tomcat的配置文件 ---catalina.policy #to…...

数学建模-滑翔伞伞翼面积的设计,运动状态计算和优化 !

我们考虑滑翔伞的伞翼面积设计问题以及运动状态描述。滑翔伞的性能主要取决于伞翼面积、气动特性以及飞行员的重量。我们的目标是建立数学模型来描述滑翔伞的运动状态,并优化伞翼面积的设计。 一、问题分析 滑翔伞在飞行过程中受到重力、升力和阻力的作用。升力和阻力与伞翼面…...

DiscuzX3.5发帖json api

参考文章&#xff1a;PHP实现独立Discuz站外发帖(直连操作数据库)_discuz 发帖api-CSDN博客 简单改造了一下&#xff0c;适配我自己的需求 有一个站点存在多个采集站&#xff0c;我想通过主站拿标题&#xff0c;采集站拿内容 使用到的sql如下 CREATE TABLE pre_forum_post_…...