学习设计模式之观察者模式,但是宝可梦
前言
作者在准备秋招中,学习设计模式,做点小笔记,用宝可梦为场景举例,有错误欢迎指出。
观察者模式
观察者模式定义了一种一对多的依赖关系,一个对象的状态改变,其他所有依赖者都会接收相应的通知。
所以,
- 何时使用: 一个对象的状态改变,其他所有依赖对象都要知道
- 意图: 定义一种一对多的依赖关系,一个对象状态改变,所有其他对象都要得到通知
- 主要解决: 在降低耦合的基础上,实现一对多的协作
观察者模式中,存在四种角色:
- 主题 (Subject): 被观察者,它要有能通知的状态,要维护一个观察者列表。
- 观望者(Observer): 观察者,接收主题的通知。需要有一个更新方法,接收到主题状态改变后调用。
- 具体主题(Concrete Subject): 主题的实现类。
- 具体观察者(Concrete Observer): 观察者的实现类,实现更新方法。
是不是觉得,四种角色突然被引入,十分抽象?别急,看看下面的故事。
1. 情景模拟
现在宝可梦研究所业务如日中天,人人都想开个研究所狠狠地赚一笔。
但是想要开宝可梦研究所,那肯定得先把宝可梦研究透啊,怎么研究呢?先抓回来!
所以新开的小木博士研究所就打算把关都地区和城都地区的宝可梦都给抓来再说。
小木博士决定,雇点训练家来抓宝可梦,于是大量的训练家都来兼职。
作为亲属的小智和小茂也来兼职帮个忙,小智负责关都地区,小茂负责成都地区。
为了控制成本,每种宝可梦抓一只就够了,所以,每当一个训练家抓到一个精灵,就要通知一下其他同行:
某个地区已经抓住了多少个精灵啦,总共还剩多少个,以免大家抓到重复。
(当然,因为小智和小茂是家属,所以开点小灶,只通知了他俩。)
于是可以引入了观察者模式的各个对象:
- 主题: 研究所的业务
- 观察者: 训练家
- 具体主题: 抓宝可梦的任务
- 具体观察者: 负责不同地区的训练家

2. 代码
先随便定义一个主题的接口
public interface SubjectLab {
}
定义观察者的接口,即训练家,其中,观察者依赖他要观察的Subject类
/*** 训练家*/
public abstract class ObserverTrainer {protected SubjectPokedex subject;public abstract void update();
}
小智和小茂分别负责各自地区
public class Satoshi extends ObserverTrainer{// 该对象观察这个Subject// 同时Subject也绑定这个对象,会发送通知public Satoshi(SubjectPokedex pokedex) {this.subject = pokedex;this.subject.employ(this);}@Overridepublic void update() {System.out.println("I am Satoshi! I got the message:");this.subject.getDexState("Kanto");}
}public class Shigeru extends ObserverTrainer{public Shigeru(SubjectPokedex subject) {this.subject = subject;this.subject.employ(this);}@Overridepublic void update() {System.out.println("I am Shigeru! I got the message:");this.subject.getDexState("Johto");}
}
最后是这次抓宝可梦任务的实现类
public class SubjectPokedex implements SubjectLab{private List<ObserverTrainer> trainers = new ArrayList<>();private HashMap<String, Integer> pokedex = new HashMap<>();private Integer num;/*** 初始化,假设各个地区已经发现了很多宝可梦* 还有value个宝可梦没有被抓到研究所研究* num统计总数*/public SubjectPokedex() {pokedex.put("Kanto", 155);pokedex.put("Johto", 155);num = 310;}/*** 打印某地区剩下的宝可梦* @param region 地区*/public void getDexState(String region){System.out.println("There are still " + pokedex.getOrDefault(region, 0) + " Pokemons in " +region + " that haven't been captured.");System.out.println("There are a total of " + num +" Pokemon that have not been captured yet.\n");}/*** 抓获了num个宝可梦*/public void catchPokemon(String region, Integer num){this.pokedex.put(region, pokedex.get(region) - num);this.num -= num;notifyAllTrainers();}/*** 任命愿意来帮助捕捉宝可梦的训练家们* @param trainer 训练家*/public void employ(ObserverTrainer trainer){trainers.add(trainer);}/*** 通知所有参与的训练家*/public void notifyAllTrainers(){for (ObserverTrainer trainer : trainers) {trainer.update();}}
}
测试类
public class ObserverDemo {public static void main(String[] args) {// Subject对象SubjectPokedex subjectPokedex = new SubjectPokedex();// 两个观察者,接收Subject的通知new Satoshi(subjectPokedex);new Shigeru(subjectPokedex);// 通知:关都地区抓住了30只subjectPokedex.catchPokemon("Kanto", 30);// 通知:城都地区抓住了90只subjectPokedex.catchPokemon("Johto", 90);}
}
I am Satoshi! I got the message:
There are still 125 Pokemons in Kanto that haven't been captured.
There are a total of 280 Pokemon that have not been captured yet.I am Shigeru! I got the message:
There are still 155 Pokemons in Johto that haven't been captured.
There are a total of 280 Pokemon that have not been captured yet.I am Satoshi! I got the message:
There are still 125 Pokemons in Kanto that haven't been captured.
There are a total of 190 Pokemon that have not been captured yet.I am Shigeru! I got the message:
There are still 65 Pokemons in Johto that haven't been captured.
There are a total of 190 Pokemon that have not been captured yet.
3.应用
笔者水平有限,暂时不知道Java哪个常用API用到了观察者模式。
不过可以根据这个思想大概猜测,一种注册端口,再监听的做法和这个差不多。
如果你的微博关注了某人,TA发微博时会推送给你,关注这个行为就相当于是你开始观察这个人(Subject).
或者Steam心愿单添加了某款游戏,这游戏降价时也会推送给你,推送给每个观察者(添加愿望单的玩家).
4.和发布-订阅模式的讨论
有了解过消息队列的聪明的训练家就会感觉:这和发布-订阅模式很像啊!
在查阅资料的过程中,发现有些博主会把发布-订阅模式与观察者模式划等号,其实细品的话还是有一些区别。
我认为最大的区别在于,发布-订阅模式的主要目的是将发布者与订阅者解耦,发布者将消息发送给中间代理,然后代理分发消息给订阅者,让订阅者和发布者之间无需互相关注。
而观察者模式,强调的是观察者和被观察者之间的联系,被观察者(Subject)甚至会负责维护一个观察者的抽象类的列表,他们之间的联系是紧密的。
第二个区别,发布-订阅模式的消息传递是通过中间代理,而观察者模式是观察者与被观察者之间直接通信。
所以发布-订阅模式是不能跟观察者模式划等号的,观察者模式是一种更为简单的设计理念,而发布-订阅模式适用于更复杂的业务场景。
相关文章:
学习设计模式之观察者模式,但是宝可梦
前言 作者在准备秋招中,学习设计模式,做点小笔记,用宝可梦为场景举例,有错误欢迎指出。 观察者模式 观察者模式定义了一种一对多的依赖关系,一个对象的状态改变,其他所有依赖者都会接收相应的通知。 所…...
课程项目设计--spring security--用户管理功能--宿舍管理系统--springboot后端
写在前面: 还要实习,每次时间好少呀,进度会比较慢一点 本文主要实现是用户管理相关功能。 前文项目建立 文章目录 验证码功能验证码配置验证码生成工具类添加依赖功能测试编写controller接口启动项目 security配置拦截器配置验证码拦截器 …...
学习设计模式之装饰器模式,但是宝可梦
装饰模式 为了不改变组件的结构,动态地扩展其功能。 通常,扩展功能通过子类进行,但是继承的方式具有静态特征,耦合度高。 意图:动态地给对象添加额外的功能 主要解决:继承方式是静态特征,扩…...
【AWS】创建IAM用户;无法登录IAM用户怎么办?错误提示:您的身份验证信息错误,请重试(已解决)
目录 0.背景问题分析 1.解决步骤 0.背景问题分析 windows 11 ,64位 我的问题情景: 首先我创建了aws的账户,并且可以用ROOT用户登录,但是在登录时选择IAM用户,输入ROOT的名字和密码,就会提示【您的身份验证…...
微服务基础知识
文章目录 微服务基础知识一、系统架构的演变1、单体应用架构2、垂直应用架构3、分布式SOA架构(1)什么是SOA(2)SOA架构 4、微服务架构5、SOA和微服务的关系(1)SOA(2)微服务架构 二、分…...
倒残差结构
倒残差结构: 倒残差结构是MobileNetV2中引入的一种设计,用于增强网络的表达能力和特征提取能力,同时保持轻量级的特点。它的核心思想是在每个瓶颈块中,先使用一个扩张卷积(Dilated Convolution)&#x…...
Docker的基本使用
Docker 概念 Docker架构 docker分为客户端,Docker服务端,仓库 客户端 Docker 是一个客户端-服务器(C/S)架构程序。Docker 客户端只需要向 Docker 服务端发起请求,服务端将完成所有的工作并返回相应结果。 Docker …...
paddlenlp安装踩坑记录
错误1 ModuleNotFoundError: No module named paddle.metric我下载paddlepaddle-gpu2.5.0.post117解决了,最开始下载的2.5.1报错,post后面的117是我的cuda版本,不要写你对应的版本号 python3 -m pip install paddlepaddle-gpu2.5.0.post117…...
微服务流程引擎:简单又灵活,实现流程全生命周期管理!
伴随着日益激烈的市场竞争,传统的办公操作已经无法满足发展需要了。如果采用微服务流程引擎加油助力,就可以帮助企业更好地管理数据资源,高效做好各种表单制作,实现高效率办公。流辰信息以市场为导向,用心钻研低代码技…...
Qt表格数据处理
概述 在Qt表格数据处理中,涉及到如下几个具体的类: QAbstractItemModel:这是一个抽象基类,定义了模型(Model)的接口规范。所有的模型类都应该派生自QAbstractItemModel,并实现它的纯虚函数&…...
EasyPOI 实战总结
EasyPOI实战总结 简介 easypoi功能如同名字easy,主打的功能就是容易,让一个没见接触过poi的人员 就可以方便的写出Excel导出,Excel模板导出,Excel导入,Word模板导出,通过简单的注解和模板 语言(熟悉的表达式语法),完成以前复杂的写法 使用EasyPOI 环境搭建 # 1.引入相关依…...
【LeetCode-困难题】42. 接雨水
题目 题解一:暴力双重for循环(以行计算水量) 1.先找出最高的柱子有多高(max 3) 2.然后第一个for为行数(1,2,3) 3.第二个for计算每一行的雨水量(关键在于去除…...
npm install 安装依赖,报错 Host key verification failed
设置 git 的身份和邮箱 git config --global user.name "你的名字" > 用户名 git config --global user.email “你的邮箱" > 邮箱进入 > 用户 > [你的用户名] > .ssh文件夹下,删除 known_hosts 文件即可 进入之后有可能会看到 known_hosts…...
SOLIDWORKS焊件是什么?
SOLIDWORKS是一款广泛应用于机械设计领域的三维计算机辅助设计软件。SOLIDWORKS提供了强大的焊件功能,可以帮助工程师们以更高的效率设计焊接件。本文将介绍SOLIDWORKS焊件的概念、特点以及使用方法,以期帮助读者更好地理解和应用这一关键技术。 SOLIDWO…...
2023国赛数学建模D题思路模型代码 高教社杯
本次比赛我们将会全程更新思路模型及代码,大家查看文末名片获取 之前国赛相关的资料和助攻可以查看 2022数学建模国赛C题思路分析_2022国赛c题matlab_UST数模社_的博客-CSDN博客 2022国赛数学建模A题B题C题D题资料思路汇总 高教社杯_2022国赛c题matlab_UST数模社…...
git协议实现管理(三个步骤)
GitHub官网访问: https://github.com/dashboard 初次使用git的用户要使用git协议大概需要三个步骤: 一、生成密钥对 二、设置远程仓库(本文以github为例)上的公钥 三、把git的remote url远程仓库URL可访问路径修改为git协议(以上两个步骤初次设置过以后,…...
“深入理解JVM:探索Java虚拟机的内部机制“
标题:深入理解JVM:探索Java虚拟机的内部机制 摘要: Java虚拟机(Java Virtual Machine,JVM)是Java语言的核心,负责将Java源代码编译成可执行的字节码并运行。本篇博客将深入探索JVM的内部机制&a…...
Unity——各种特效的基本使用方法
特效是游戏制作不可或缺的一环,作为游戏开发者最重要的工作就是将特效添加到游戏中,并在合适的时机、合适的位置将特效播放出来,同时还要注意特效的管理和销毁。 某些种类的特效,如动效、贴花,还要编写脚本代码以实现…...
smiley-http-proxy-servlet 实现springboot 反向代理,结合项目鉴权,安全的引入第三方项目服务
项目中反向代理 集成第三方的服务接口或web监控界面,并实现与自身项目相结合的鉴权方法 依赖 smiley-http-proxy-servlet GitHub链接 2.0 版开始,代理切换到jakarta servlet-api<!--HTTP 代理 Servlet--><dependency><groupId>org.mit…...
(vue)多级表头且转为百分比显示
(vue)多级表头且转为百分比显示 <el-table-column align"center" label"近三个月数据情况"><el-table-column align"center" prop"amount" :label"tableLast[0]"><template slot-scope"{ row }"&g…...
【OSG学习笔记】Day 18: 碰撞检测与物理交互
物理引擎(Physics Engine) 物理引擎 是一种通过计算机模拟物理规律(如力学、碰撞、重力、流体动力学等)的软件工具或库。 它的核心目标是在虚拟环境中逼真地模拟物体的运动和交互,广泛应用于 游戏开发、动画制作、虚…...
Java 8 Stream API 入门到实践详解
一、告别 for 循环! 传统痛点: Java 8 之前,集合操作离不开冗长的 for 循环和匿名类。例如,过滤列表中的偶数: List<Integer> list Arrays.asList(1, 2, 3, 4, 5); List<Integer> evens new ArrayList…...
《从零掌握MIPI CSI-2: 协议精解与FPGA摄像头开发实战》-- CSI-2 协议详细解析 (一)
CSI-2 协议详细解析 (一) 1. CSI-2层定义(CSI-2 Layer Definitions) 分层结构 :CSI-2协议分为6层: 物理层(PHY Layer) : 定义电气特性、时钟机制和传输介质(导线&#…...
SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)
上一章用到了V2 的概念,其实 Fiori当中还有 V4,咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务),代理中间件(ui5-middleware-simpleproxy)-CSDN博客…...
视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)
前言: 最近在做行为检测相关的模型,用的是时空图卷积网络(STGCN),但原有kinetic-400数据集数据质量较低,需要进行细粒度的标注,同时粗略搜了下已有开源工具基本都集中于图像分割这块,…...
JVM 内存结构 详解
内存结构 运行时数据区: Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器: 线程私有,程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 每个线程都有一个程序计数…...
Linux 内存管理实战精讲:核心原理与面试常考点全解析
Linux 内存管理实战精讲:核心原理与面试常考点全解析 Linux 内核内存管理是系统设计中最复杂但也最核心的模块之一。它不仅支撑着虚拟内存机制、物理内存分配、进程隔离与资源复用,还直接决定系统运行的性能与稳定性。无论你是嵌入式开发者、内核调试工…...
基于PHP的连锁酒店管理系统
有需要请加文章底部Q哦 可远程调试 基于PHP的连锁酒店管理系统 一 介绍 连锁酒店管理系统基于原生PHP开发,数据库mysql,前端bootstrap。系统角色分为用户和管理员。 技术栈 phpmysqlbootstrapphpstudyvscode 二 功能 用户 1 注册/登录/注销 2 个人中…...
车载诊断架构 --- ZEVonUDS(J1979-3)简介第一篇
我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 做到欲望极简,了解自己的真实欲望,不受外在潮流的影响,不盲从,不跟风。把自己的精力全部用在自己。一是去掉多余,凡事找规律,基础是诚信;二是…...
前端调试HTTP状态码
1xx(信息类状态码) 这类状态码表示临时响应,需要客户端继续处理请求。 100 Continue 服务器已收到请求的初始部分,客户端应继续发送剩余部分。 2xx(成功类状态码) 表示请求已成功被服务器接收、理解并处…...
