学习设计模式之观察者模式,但是宝可梦
前言
作者在准备秋招中,学习设计模式,做点小笔记,用宝可梦为场景举例,有错误欢迎指出。
观察者模式
观察者模式定义了一种一对多的依赖关系,一个对象的状态改变,其他所有依赖者都会接收相应的通知。
所以,
- 何时使用: 一个对象的状态改变,其他所有依赖对象都要知道
- 意图: 定义一种一对多的依赖关系,一个对象状态改变,所有其他对象都要得到通知
- 主要解决: 在降低耦合的基础上,实现一对多的协作
观察者模式中,存在四种角色:
- 主题 (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…...
Vim 调用外部命令学习笔记
Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...

多模态2025:技术路线“神仙打架”,视频生成冲上云霄
文|魏琳华 编|王一粟 一场大会,聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中,汇集了学界、创业公司和大厂等三方的热门选手,关于多模态的集中讨论达到了前所未有的热度。其中,…...
基于大模型的 UI 自动化系统
基于大模型的 UI 自动化系统 下面是一个完整的 Python 系统,利用大模型实现智能 UI 自动化,结合计算机视觉和自然语言处理技术,实现"看屏操作"的能力。 系统架构设计 #mermaid-svg-2gn2GRvh5WCP2ktF {font-family:"trebuchet ms",verdana,arial,sans-…...

抖音增长新引擎:品融电商,一站式全案代运营领跑者
抖音增长新引擎:品融电商,一站式全案代运营领跑者 在抖音这个日活超7亿的流量汪洋中,品牌如何破浪前行?自建团队成本高、效果难控;碎片化运营又难成合力——这正是许多企业面临的增长困局。品融电商以「抖音全案代运营…...

2021-03-15 iview一些问题
1.iview 在使用tree组件时,发现没有set类的方法,只有get,那么要改变tree值,只能遍历treeData,递归修改treeData的checked,发现无法更改,原因在于check模式下,子元素的勾选状态跟父节…...
uniapp中使用aixos 报错
问题: 在uniapp中使用aixos,运行后报如下错误: AxiosError: There is no suitable adapter to dispatch the request since : - adapter xhr is not supported by the environment - adapter http is not available in the build 解决方案&…...

(转)什么是DockerCompose?它有什么作用?
一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行容器。 Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...

AI,如何重构理解、匹配与决策?
AI 时代,我们如何理解消费? 作者|王彬 封面|Unplash 人们通过信息理解世界。 曾几何时,PC 与移动互联网重塑了人们的购物路径:信息变得唾手可得,商品决策变得高度依赖内容。 但 AI 时代的来…...

GruntJS-前端自动化任务运行器从入门到实战
Grunt 完全指南:从入门到实战 一、Grunt 是什么? Grunt是一个基于 Node.js 的前端自动化任务运行器,主要用于自动化执行项目开发中重复性高的任务,例如文件压缩、代码编译、语法检查、单元测试、文件合并等。通过配置简洁的任务…...
Vite中定义@软链接
在webpack中可以直接通过符号表示src路径,但是vite中默认不可以。 如何实现: vite中提供了resolve.alias:通过别名在指向一个具体的路径 在vite.config.js中 import { join } from pathexport default defineConfig({plugins: [vue()],//…...