Java设计模式—观察者模式
观察者模式
目录
- 观察者模式
- 1、什么是观察者模式?
- 2、观察者模式优缺点及注意事项?
- 3、观察者模式实现?
- 4、手写线程安全的观察者模式?
1、什么是观察者模式?
- 实例:现实生活中很多事物都是依赖存在的,一个发生变化会影响很多事物。比如油价上涨,关系很多企业,很多家庭;红绿灯发生变化时,人们会停止,会前进等。
- 观察者模式 (Observer Pattern) :是一种一对多的依赖关系,让多个观察对象同时监听某一个主题对象,当主题对象的状态发生变化时,会自动通知所有观察者,使得它们能够自动更新自己。适用于当一个对象的状态发生改变时,所有依赖于它的对象都需要得到通知的情况。
- 观察者模式具体的角色 :实现观察者模式时要注意具体目标对象和具体观察者对象之间不能直接调用,否则将使两者之间紧密耦合起来,这违反了面向对象的设计原则。
- 抽象主题(Subject)角色:也叫抽象目标类,它提供了一个用于保存观察者对象的聚集类和增加、删除观察者对象的方法,以及通知所有观察者的抽象方法。
- 具体主题(Concrete Subject)角色:也叫具体目标类,它实现抽象目标中的通知方法,当具体主题的内部状态发生改变时,通知所有注册过的观察者对象。继承Subject类,在这里实现具体业务,在具体项目中,该类会有很多变种。
- 抽象观察者(Observer)角色:它是一个抽象类或接口,它包含了一个更新自己的抽象方法,当接到具体主题的更改通知时被调用。
- 具体观察者(Concrete Observer)角色:实现抽象观察者中定义的抽象方法,以便在得到目标的更改通知时更新自身的状态。
2、观察者模式优缺点及注意事项?
- 优点:
1.降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系.符合依赖倒置原则
2.目标与观察者之间建立了一套触发机制
- 缺点:
1.目标与观察者之间的依赖关系并没有完全解除,而且有可能出现循环引用
2.当观察者对象很多时,通知的发布会花费很多时间,影响程序的效率
- 注意事项:
1.JDK8 中java.util.Observable 类和 java.util.Observer 接口定义了观察者模式,后面版本舍弃了,因为在使用异步处理的情况下,线程不安全。
2.要注意循环调用情况,避免死锁。
3.可以去观察消息队列实现,典型的观察者模式实现。
3、观察者模式实现?
模仿jdk8中 Observable 类 和 Observer接口实现
- 目标类
public class Subject {private Vector<Observer> obs = new Vector<>();public void addObserver(Observer o) {if (o == null)throw new NullPointerException();if (!obs.contains(o)) {obs.add(o);}}public void deleteObserver(Observer o) {obs.remove(o);}public void notifyObservers() {for(Observer observer : this.obs) {observer.resopnse();}}
}
- 具体实现目标类
public class ConcreteSubject extends Subject{// 具体实现public void doSomethings(){// 一些其它逻辑处理System.out.println("-----被观察者发生了改变------");super.notifyObservers();}
}
- 观察者类
public interface Observer {// 响应void resopnse();
}
- 具体观察者类
public class ConcreteObserver implements Observer{private int num;public ConcreteObserver(int num) {this.num = num;}@Overridepublic void resopnse() {System.out.println("观察者"+num+"做出改变");}
}
- 执行任务
public class Main {public static void main(String[] args) {
// --------------- 观察者模式 -----------------// 先创建多个观察者ConcreteSubject concreteSubject = new ConcreteSubject();for (int a = 0; a < 10; a++) {concreteSubject.addObserver(new ConcreteObserver(a+1));}concreteSubject.doSomethings();}
4、手写线程安全的观察者模式?
设定场景:手机花费快没钱了,运营商通知你要缴费了。 这里运营商就是被观察者,用户就是观察者,用户的手机号就是注册的一个过程,运营商通知用户。
使用jdk中com.util.concurrent提供的线程安全的类:CountDownLatch,CountDownLatch就一个线程同步工具,它相当于一个倒序计数器,用来协调多个线程的执行。多个线程通过调用它们所共享的计数器CountDownLatch的countDown方法来让计数器减1。通过await方法来阻塞当前线程,直到计数器变成0。达到线程阻塞直至其他线程执行完成被重新唤醒。主要有三个方法:
1.构造函数,初始化state的值,state等于同步线程数
2.await(),让线程阻塞
3.countDown(),计数器(state)减1的方法。
public class Main {public static void main(String[] args) {// --------------- 安全的观察者模式 --------------// 设定场景:手机花费快没钱了,运营商通知你要缴费了。 这里运营商就是被观察者,用户就是观察者,用户的手机号就是注册的一个过程,运营商通知用户。ConcreteTelecomOperator telecomOperator = new ConcreteTelecomOperator("中国电信");telecomOperator.addUser(new ConcreteUser("张三"));telecomOperator.addUser(new ConcreteUser("李四"));telecomOperator.addUser(new ConcreteUser("王五"));telecomOperator.addUser(new ConcreteUser("小明"));telecomOperator.doSomeThing("通知您,您已欠费请及时缴费。祝你生活愉快!");}
}
/*** @program: practice_tools* @description: 运营商目标类* @author: tiezhu* @create: 2025-01-20 09:58**/
public class TelecomOperator {private String name;private ConcurrentMap<String, User> obs;public TelecomOperator(String name) {this.name = name;obs = new ConcurrentHashMap<>();}public void addUser(User u){if (u == null)throw new NullPointerException();if (!obs.containsKey(u.getName())) {obs.put(u.getName(),u);}}public void removeUser(User u){obs.remove(u.getName());}public void notifyObservers(String content) {try {long beginTime = System.currentTimeMillis();CountDownLatch latch = new CountDownLatch(obs.size());for (User user : obs.values()) {user.response(content);latch.countDown();}latch.await();long endTime = System.currentTimeMillis();System.out.println(name + "消息发送完毕,耗时:" + (endTime - beginTime));System.out.println();} catch (InterruptedException e) {e.printStackTrace();}}
}
/*** @program: practice_tools* @description: 具体目标类* @author: tiezhu* @create: 2025-01-20 11:43**/
public class ConcreteTelecomOperator extends TelecomOperator{public ConcreteTelecomOperator(String name) {super(name);}public void doSomeThing(String content){System.out.println("开始发布信息:");super.notifyObservers(content);}
}
/*** 用户观察者*/
public interface User {void response(String content);String getName();
}
/*** @program: practice_tools* @description: 观察者实体类* @author: tiezhu* @create: 2025-01-20 10:04**/
public class ConcreteUser implements User{private String name;public ConcreteUser(String name) {this.name = name;}@Overridepublic void response(String content) {System.out.println("接收到了消息为:"+content);System.out.println(name + "准备去缴费了");}@Overridepublic String getName() {return name;}
}
结果:执行结果和创建顺序不同,不用按照创建顺序执行完再执行了。

相关文章:
Java设计模式—观察者模式
观察者模式 目录 观察者模式1、什么是观察者模式?2、观察者模式优缺点及注意事项?3、观察者模式实现?4、手写线程安全的观察者模式? 1、什么是观察者模式? - 实例:现实生活中很多事物都是依赖存在的&#x…...
人工智能在数字化转型中的角色:从数据分析到智能决策
引言 在数字化转型浪潮中,人工智能(AI)正迅速崛起,成为推动企业创新和变革的关键力量。面对日益复杂的市场环境和激烈的行业竞争,企业亟需借助技术手段提高运营效率、优化决策过程,并增强市场竞争力。而AI…...
论文阅读 Multi-view Classification Using Hybrid Fusion and Mutual Distillation
Multi-view Classification Using Hybrid Fusion and Mutual Distillation Intro 多视角问题可以分为两类: Structured。固定视角,或预先定义的视角的问题。unstructured。 本文的三大contributions: 引入了混合的多视角融合策略。使用了…...
AIGC浪潮下,图文内容社区数据指标体系如何构建?
文章目录 01 案例:以图文内容社区为例实践数据指标体构建02 4个步骤实现数据指标体系构建1. 明确业务目标,梳理北极星指标2. 梳理业务流程,明确过程指标3. 指标下钻分级,构建多层级数据指标体系4. 添加分析维度,构建完…...
”彩色的验证码,使用pytesseract识别出来的验证码内容一直是空“的解决办法
问题:彩色的验证码,使用pytesseract识别出来的验证码内容一直是空字符串 原因:pytesseract只识别黑色部分的内容 解决办法:先把彩色图片精确转换成黑白图片。再将黑白图片进行反相,将验证码部分的内容变成黑色&#…...
前端Vue2项目使用md编辑器
项目中有一个需求,要在前端给用户展示内容,内容有 AI 生成的,返回来的是 md 格式,所以需要给用户展示 md 格式,并且管理端也可以编辑这个 md 格式的文档。 使用组件库 v-md-editor。 https://code-farmer-i.github.i…...
OpenVela 架构剖析:从内核到应用
目录 一、总体架构概述 二、 内核层 2.1. OpenVela架构的内核基础 2.2. 内核层的主要职责 2.3. OpenVela对NuttX的扩展与优化 三、系统服务层 2.1. 进程管理 2.2. 内存管理 2.3. 文件系统 2.4. 网络通信 四、框架层 4.1. 模块化设计 4.2. API接口 4.3. 组件和服务…...
vue视频流播放,支持多种视频格式,如rmvb、mkv
先将视频转码为ts ffmpeg -i C:\test\3.rmvb -codec: copy -start_number 0 -hls_time 10 -hls_list_size 0 -f hls C:\test\a\output.m3u8 后端配置接口 import org.springframework.core.io.Resource; import org.springframework.core.io.UrlResource; import org.spring…...
记一个Timestamp时区问题的坑
resultSet.getTimestamp(“kpi_collect_time”)查出来的Timestamp居然是带时区的, 如果该Timestamp不是UTC时区的,Timestamp.toInstant().atZone(ZoneId.of(“UTC”))会把Timestamp转成UTC时区 使用Timestamp.toLocalDateTime()可以直接把时区信息抹除 …...
新年好(Dijkstra+dfs/全排列)
1135. 新年好 - AcWing题库 思路: 1.先预处理出1,a,b,c,d,e到其他点的单源最短路,也就是进行6次Dijkstra 2.计算以1为起点的这6个数的全排列,哪种排列方式所得距离最小,也可以使用dfs 1.Dijkstradfs #define int long longusing …...
如何“看到” Spring 容器?
Spring 容器是一个运行时的抽象工具,用来管理 Bean 的生命周期和依赖。虽然它本身不可直接观察,但可以通过以下方式间接“看到”容器的内容或行为。 2.1 容器是如何实例化的? Spring 容器的实例化是通过 ApplicationContext 或 BeanFactory …...
怎么使用CRM软件?操作方法和技巧有哪些?
什么是CRM? 嘿,大家好!你知道吗,在当今这个数字化时代里,我们每天都在与各种各样的客户打交道。无论是大公司还是小型企业,都希望能够更好地管理这些关系并提高业务效率。这时候就轮到我们的“老朋友”——…...
Spingboot整合Netty,简单示例
Netty介绍在文章末尾 Netty介绍 项目背景 传统socket通信,有需要自身管理整个状态,业务繁杂等问题。 pom.xml <dependency><groupId>io.netty</groupId><artifactId>netty-all</artifactId><version>4.1.117.F…...
grafana新增email告警
选择一个面板 比如cpu 新增一个临界点表达式 input选A 就是A的值达到某个临界点 触发告警 我这边IS ABOVE0.15就是cpu大于0.15%就触发报警,这个值怎么填看指标的值显示 这里要设置一下报警条件 这边随便配置下 配置标签和通知,选择你的邮件 看下告警…...
Github 2025-01-20 开源项目周报 Top15
根据Github Trendings的统计,本周(2025-01-20统计)共有15个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Python项目10Rust项目2TypeScript项目1C++项目1Jupyter Notebook项目1Go项目1Tabby: 自托管的AI编码助手 创建周期:310 天开发语言:Rust协议类…...
【Rabbitmq】Rabbitmq高级特性-发送者可靠性
Rabbitmq发送者可靠性 发送者重连发送者确认1.开启确认机制2.ReturnCallback3.ConfirmCallback MQ的可靠性数据持久化交换机持久化队列持久化消息持久化 Lazy Queue 总结其他文章 Rabbitmq提供了两种发送来保证发送者的可靠性,第一种叫发送者重连,第二种…...
K8S中Service详解(一)
Service介绍 在Kubernetes中,Service资源解决了Pod IP地址不固定的问题,提供了一种更稳定和可靠的服务访问方式。以下是Service的一些关键特性和工作原理: Service的稳定性:由于Pod可能会因为故障、重启或扩容而获得新的IP地址&a…...
Effective C++读书笔记——item23(用非成员,非友元函数取代成员函数)
一、主要观点: 在某些情况下,使用 non-member、non-friend 函数来替换 member 函数可以增强封装性和可扩展性,提供更好的软件设计。 二、详细解释: 封装性: 类成员函数的封装性考量:成员函数可以访问类的…...
云原生前端开发:打造现代化高性能的用户体验
引言:前端开发的新风向 在过去的几年中,前端开发领域经历了快速的演变,从早期的静态网页到如今复杂的单页应用(SPA),再到微前端架构和渐进式Web应用(PWA),前端技术一直处…...
循环队列(C语言版)
循环队列(C语言版) 1.简单介绍循环队列2.使用何种结构来实现3.基本结构4.初始化5.判空判满6.向循环队列插入一个元素7.从循环队列中删除一个元素8.获取队头队尾元素9.释放空间10.完整代码 🌟🌟hello,各位读者大大们你们好呀&#…...
利用ngx_stream_return_module构建简易 TCP/UDP 响应网关
一、模块概述 ngx_stream_return_module 提供了一个极简的指令: return <value>;在收到客户端连接后,立即将 <value> 写回并关闭连接。<value> 支持内嵌文本和内置变量(如 $time_iso8601、$remote_addr 等)&a…...
前端倒计时误差!
提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...
Docker 本地安装 mysql 数据库
Docker: Accelerated Container Application Development 下载对应操作系统版本的 docker ;并安装。 基础操作不再赘述。 打开 macOS 终端,开始 docker 安装mysql之旅 第一步 docker search mysql 》〉docker search mysql NAME DE…...
站群服务器的应用场景都有哪些?
站群服务器主要是为了多个网站的托管和管理所设计的,可以通过集中管理和高效资源的分配,来支持多个独立的网站同时运行,让每一个网站都可以分配到独立的IP地址,避免出现IP关联的风险,用户还可以通过控制面板进行管理功…...
【JavaSE】多线程基础学习笔记
多线程基础 -线程相关概念 程序(Program) 是为完成特定任务、用某种语言编写的一组指令的集合简单的说:就是我们写的代码 进程 进程是指运行中的程序,比如我们使用QQ,就启动了一个进程,操作系统就会为该进程分配内存…...
STM32HAL库USART源代码解析及应用
STM32HAL库USART源代码解析 前言STM32CubeIDE配置串口USART和UART的选择使用模式参数设置GPIO配置DMA配置中断配置硬件流控制使能生成代码解析和使用方法串口初始化__UART_HandleTypeDef结构体浅析HAL库代码实际使用方法使用轮询方式发送使用轮询方式接收使用中断方式发送使用中…...
提升移动端网页调试效率:WebDebugX 与常见工具组合实践
在日常移动端开发中,网页调试始终是一个高频但又极具挑战的环节。尤其在面对 iOS 与 Android 的混合技术栈、各种设备差异化行为时,开发者迫切需要一套高效、可靠且跨平台的调试方案。过去,我们或多或少使用过 Chrome DevTools、Remote Debug…...
2025年- H71-Lc179--39.组合总和(回溯,组合)--Java版
1.题目描述 2.思路 当前的元素可以重复使用。 (1)确定回溯算法函数的参数和返回值(一般是void类型) (2)因为是用递归实现的,所以我们要确定终止条件 (3)单层搜索逻辑 二…...
如何把工业通信协议转换成http websocket
1.现状 工业通信协议多数工作在边缘设备上,比如:PLC、IOT盒子等。上层业务系统需要根据不同的工业协议做对应开发,当设备上用的是modbus从站时,采集设备数据需要开发modbus主站;当设备上用的是西门子PN协议时…...
stm32进入Infinite_Loop原因(因为有系统中断函数未自定义实现)
这是系统中断服务程序的默认处理汇编函数,如果我们没有定义实现某个中断函数,那么当stm32产生了该中断时,就会默认跑这里来了,所以我们打开了什么中断,一定要记得实现对应的系统中断函数,否则会进来一直循环…...
