《游戏编程模式》学习笔记(四) 观察者模式 Observer Pattern
定义
观察者模式定义了对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
这是定义,看不懂就看不懂吧,我接下来举个例子慢慢说
为什么我们需要观察者模式
我们看一个很简单的需求,现在要你在游戏中加入成就系统,在物体坠落1000米的时候给玩家发一个成就勋章,你要这么做?
最直观的方法就是,在游戏的物理系统那一部分中,加入这么一段代码:
void Physics::updateEntity(Entity& entity)
{bool wasOnSurface = entity.isOnSurface();entity.accelerate(GRAVITY);entity.update();if (wasOnSurface && !entity.isOnSurface()){if (surface.height - entity.height > 1000){//解锁成就unlockFallOffBridge();}}
}
咋一看是不是还行?就加了几行而已。
那么如果我还要求你播放坠落音效呢?是不是还得这样写:
void Physics::updateEntity(Entity& entity)
{bool wasOnSurface = entity.isOnSurface();entity.accelerate(GRAVITY);entity.update();if (wasOnSurface && !entity.isOnSurface()){if (surface.height - entity.height > 1000){//解锁成就unlockFallOffBridge();//播放音效playfallmusic();}}
}
这样看也还行,那如果组长让你根据物体撞击不同的地面,播放不同的地面音效,那这段代码是不是又得膨胀了:
void Physics::updateEntity(Entity& entity)
{bool wasOnSurface = entity.isOnSurface();entity.accelerate(GRAVITY);entity.update();if (wasOnSurface && !entity.isOnSurface()){if (surface.height - entity.height > 1000){//解锁成就unlockFallOffBridge();//播放音效if (hitground){playhitgroundmusic();}if (hitwater){playhitwatermusic();}//.....}}
}
要知道,这可是在你的游戏的物理引擎中,我们并不想看到在处理撞击代码的线性代数时, 有出现关于成就系统,音效系统的调用是不?我们喜欢的是,照旧,让关注游戏一部分的所有代码集成到一块。我们想要解耦物理系统和这些不相关的东西。
这就是观察者模式出现的原因。 这让代码宣称有趣的事情发生了,而不必关心到底是谁接受了通知。
一旦你使用了观察者模式,你的代码就会变成这样:
void Physics::updateEntity(Entity& entity)
{bool wasOnSurface = entity.isOnSurface();entity.accelerate(GRAVITY);entity.update();if (wasOnSurface && !entity.isOnSurface()){notify(entity, EVENT_START_FALL);}
}
是不是简洁了很多很多?比刚才那一大堆丑陋的代码好看多了。
观察者模式做的就是声称,“额,我不知道有谁感兴趣,但是这个东西刚刚掉下去了。做你想做的事吧。”
可能有人会说,诶,这也没有完全解耦啊。的确,物理引擎确实决定了要发送什么通知,所以这并没有完全解耦。但在架构这个领域,通常只能让系统变得更好,而不是完美。
如何构建观察者模式?
最传统的构建方式就是这样,使用对象模式构建观察者
我们先写一个基础的观察者抽象基类
class Observer
{
public:virtual ~Observer() {}virtual void onNotify(const Entity& entity, Event event) = 0;
};
然后让我们的成就系统和音效系统等想成为观察者的系统都继承这个基类:
class Achievements : public Observer
{
public:virtual void onNotify(const Entity& entity, Event event){switch (event){case EVENT_ENTITY_FELL:if (entity.isHero() && heroIsOnBridge_){unlock(ACHIEVEMENT_FELL_OFF_BRIDGE);}break;// 处理其他事件,更新heroIsOnBridge_变量……}}private:void unlock(Achievement achievement){// 如果还没有解锁,那就解锁成就……}bool heroIsOnBridge_;
};
对于被观察者,如物理系统中,我们只要让它持有这个observer的指针就好了,一旦出现了某些事件,我们就给这些指针指向的observer发消息。
为了正式一点,让所有可能的系统都成为被观察者,我们写一个叫subject的基类,让所有想成为被观察者的系统都可以继承这个基类来成为被观察者。
class Subject
{
public:void addObserver(Observer* observer){// 添加到数组中……}void removeObserver(Observer* observer){// 从数组中移除……}void removeObserver(Observer* observer){// 从数组中移除……}
protected:void notify(const Entity& entity, Event event){for (int i = 0; i < numObservers_; i++){observers_[i]->onNotify(entity, event);}}private:Observer* observers_[MAX_OBSERVERS];int numObservers_;
};
我们可以看见,这里写了一个观察者数组,存了许多观察者的指针,这是因为大部分情况下,被观察者可能会有好多个观察者观察着它。然后我们也写了一些方法来增删这个数组。
然后就是面向对象的东西了,我们让物理系统继承这个基类
class Physics : public Subject
{
public:void updateEntity(Entity& entity);
};
现在,当物理引擎做了些值得关注的事情,它调用notify(),就像之前的例子。 它遍历了观察者列表,通知所有观察者。

恭喜你已经掌握了如何写一个观察者模式,你所看到的就是一个观察者模式的全部。现在来回顾一下定义:
观察者模式定义了对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
是不是有点明白了?
**
观察者模式的使用场合
**
当一个抽象模式有两个方面,其中一个方面依赖于另一个方面,需要将这两个方面分别封装到独立的对象中,彼此独立地改变和复用的时候。
当一个系统中一个对象的改变需要同时改变其他对象内容,但是又不知道待改变的对象到底有多少个的时候。
当一个对象的改变必须通知其他对象作出相应的变化,但是不能确定通知的对象是谁的时候。
观察者模式的缺点:
- 由于观察者模式调用了一些虚方法,终究会比静态调用慢一些。
- 观察者模式是同步的。 被观察者直接调用了观察者,这意味着直到所有观察者的通知方法返回后, 被观察者才会继续自己的工作。观察者会阻塞被观察者的运行。
- 由于被观察者维护了一个数组来存储观察者指针,在实际情况中一般会用动态数组而不是这次例子中的静态数组。这样就会做出太多的动态分配。解决方法还是有的,那就是使用链表而不是数组来存储观察者指针(反正你都得遍历发通知,这俩差不多)。
原文链接:https://gpp.tkchu.me/observer.html
相关文章:
《游戏编程模式》学习笔记(四) 观察者模式 Observer Pattern
定义 观察者模式定义了对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。 这是定义,看不懂就看不懂吧,我接下来举个例子慢慢说 为什么我们需要观察者模式 我们看一个很简…...
前端一键升级 package.json里面的依赖包管理
升级需谨慎 前端一键升级 package.json里面的依赖包管理 安装:npm-check-updates npm i npm-check-updates -g缩写 ncu 在项目根目录里面执行 ncu 如图:...
当速度很重要时:使用 Hazelcast 和 Redpanda 进行实时流处理
在本教程中,了解如何构建安全、可扩展、高性能的应用程序,以释放实时数据的全部潜力。 在本教程中,我们将探索 Hazelcast 和 Redpanda 的强大组合,以构建对实时数据做出反应的高性能、可扩展和容错的应用程序。 Redpanda 是一个流…...
筛法求欧拉函数
思路: (1)若要分别求1~n每个数的欧拉函数值,则复杂度O(n*n^0.5),超时; (2)于是考虑用欧拉筛进行求取; (3)欧拉筛:基于线…...
consul限制注册的ip
假设当前服务器的ip是:192.168.56.130 1、允许 所有ip 注册(验证可行) consul agent -server -ui -bootstrap-expect1 -data-dir/usr/local/consul -nodedevmaster -advertise192.168.56.130 -bind0.0.0.0 -client0.0.0.0 2、只允许 当前ip 注册 consul agent -…...
用AI攻克“智能文字识别创新赛题”,这场大学生竞赛掀起了什么风潮?
文章目录 一、前言1.1 大赛介绍1.2 项目背景 二、基于智能文字场景个人财务管理创新应用2.1 作品方向2.2 票据识别模型2.2.1 文本卷积神经网络TextCNN2.2.2 Bert 预训练微调2.2.3 模型对比2.2.4 效果展示 2.3 票据文字识别接口 三、未来展望 一、前言 1.1 大赛介绍 中国大学生…...
EJB基本概念和使用
一、EJB是什么? EJB是sun的JavaEE服务器端组件模型,是一种规范,设计目标与核心应用是部署分布式应用程序。EJB2.0过于复杂,EJB3.0的推出减轻了开发人员进行底层开发的工作量,它取消或最小化了很多(以前这些是必须实现)…...
神经网络基础-神经网络补充概念-09-m个样本的梯度下降
概念 当应用梯度下降算法到具有 m 个训练样本的逻辑回归问题时,我们需要对每个样本计算梯度并进行平均,从而更新模型参数。这个过程通常称为批量梯度下降(Batch Gradient Descent)。 代码实现 import numpy as npdef sigmoid(z…...
分布式 - 消息队列Kafka:Kafka消费者分区再均衡(Rebalance)
文章目录 01. Kafka 消费者分区再均衡是什么?02. Kafka 消费者分区再均衡的触发条件?03. Kafka 消费者分区再均衡的过程?04. Kafka 如何判定消费者已经死亡?05. Kafka 如何避免消费者的分区再均衡?06. Kafka 消费者分区再均衡有什…...
BIO、NIO和AIO
一.引言 何为IO 涉及计算机核心(CPU和内存)与其他设备间数据迁移的过程,就是I/O。数据输入到计算机内存的过程即输入,反之输出到外部存储(比如数据库,文件,远程主机)的过程即输出。 I/O 描述了计算机系统…...
理解 Go 中的切片:append 操作的深入分析(篇1)
理解 Go 语言中 slice 的性质对于编程非常有益。下面,我将通过两个代码示例来解释切片在不同函数之间传递并执行 append 操作时的具体表现。 本篇为第 1 篇,当切片的容量 cap 充足时 第一份代码 slice1 的初始长度为 3,容量为 10 func main()…...
由于找不到mfc140u.dll,无法继续执行代码怎么修复?
当我在使用某个应用程序时遇到了mfc140u.dll缺失的错误提示时,我意识到这是由于该动态链接库文件丢失或损坏所引起的。mfc140u.dll是MFC的一部分,它包含了许多与用户界面、窗口管理、控件等相关的函数和类。这个文件通常用于支持使用MFC开发的应用程序的…...
【0.1】lubancat鲁班猫4刷入debian网络ping 域名不通问题
目录 1. 环境2. 操作步骤 1. 环境 lubancat4鲁班猫4 (4G0)不带emmc系统镜像lubancat-rk3588-debian11-gnome-20230807_update.img官方资料地址https://doc.embedfire.com/products/link/zh/latest/linux/ebf_lubancat.html 2. 操作步骤 从官网给的百度网盘下载linux系统全部…...
KafkaStream:基本使用
简介: kafkaStream:提供了对存储在kafka中的数据进行流式处理和分析的功能 特点: KafkasSream提供了一个非常简单轻量的Library,它可以非常方便的嵌入到java程序中,也可以任何方式打包部署 入门案例: 1、…...
【数据结构】二叉树
完全二叉树 是指所有结点度数小于等于2的树 所以这种情况也是: 几条性质 一个具有n个结点的完全二叉树的深度为: log 2 ( n 1 ) 的结果向上取整。 \\\log_{2}(n1) \ \ 的结果向上取整。 log2(n1) 的结果向上取整。设度为0的结点个数是n0&#…...
基于灰狼优化(GWO)、帝国竞争算法(ICA)和粒子群优化(PSO)对梯度下降法训练的神经网络的权值进行了改进(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...
jenkins自动化构建保姆级教程(持续更新中)
1.安装 1.1版本说明 访问jenkins官网 https://www.jenkins.io/,进入到首页 点击【Download】按钮进入到jenkins下载界面 左侧显示的是最新的长期支持版本,右侧显示的是最新的可测试版本(可能不稳定),建议使用最新的…...
HTTPS 的加密流程
目录 一、HTTPS是什么? 二、为什么要加密 三、"加密" 是什么 四、HTTPS 的工作过程 1.对称加密 2.非对称加密 3.中间人攻击 4.证书 总结 一、HTTPS是什么? HTTPS (Hyper Text Transfer Protocol Secure) 是基于 HTTP 协议之上的安全协议&…...
Jmeter 参数化的几种方法
目录 配置元件-用户自定义变量 前置处理器-用户参数 配置元件-CSV Data Set Config Tools-函数助手 配置元件-用户自定义变量 可在测试计划、线程组、HTTP请求下创建用户定义的变量 全局变量,可以跨线程组调用 jmeter执行的时候,只获取一次࿰…...
剑指Offer45.把数组排成最小的数 C++
1、题目描述 输入一个非负整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。 示例 1: 输入: [10,2] 输出: “102” 示例 2: 输入: [3,30,34,5,9] 输出: “3033459” 2、VS2019上运行 先转换成字符串再组合起来 #in…...
CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型
CVPR 2025 | MIMO:支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题:MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者:Yanyuan Chen, Dexuan Xu, Yu Hu…...
简易版抽奖活动的设计技术方案
1.前言 本技术方案旨在设计一套完整且可靠的抽奖活动逻辑,确保抽奖活动能够公平、公正、公开地进行,同时满足高并发访问、数据安全存储与高效处理等需求,为用户提供流畅的抽奖体验,助力业务顺利开展。本方案将涵盖抽奖活动的整体架构设计、核心流程逻辑、关键功能实现以及…...
Spring Boot 实现流式响应(兼容 2.7.x)
在实际开发中,我们可能会遇到一些流式数据处理的场景,比如接收来自上游接口的 Server-Sent Events(SSE) 或 流式 JSON 内容,并将其原样中转给前端页面或客户端。这种情况下,传统的 RestTemplate 缓存机制会…...
SCAU期末笔记 - 数据分析与数据挖掘题库解析
这门怎么题库答案不全啊日 来简单学一下子来 一、选择题(可多选) 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘:专注于发现数据中…...
UDP(Echoserver)
网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法:netstat [选项] 功能:查看网络状态 常用选项: n 拒绝显示别名&#…...
前端导出带有合并单元格的列表
// 导出async function exportExcel(fileName "共识调整.xlsx") {// 所有数据const exportData await getAllMainData();// 表头内容let fitstTitleList [];const secondTitleList [];allColumns.value.forEach(column > {if (!column.children) {fitstTitleL…...
【2025年】解决Burpsuite抓不到https包的问题
环境:windows11 burpsuite:2025.5 在抓取https网站时,burpsuite抓取不到https数据包,只显示: 解决该问题只需如下三个步骤: 1、浏览器中访问 http://burp 2、下载 CA certificate 证书 3、在设置--隐私与安全--…...
从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)
设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile,新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)
宇树机器人多姿态起立控制强化学习框架论文解析 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一) 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...
根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:
根据万维钢精英日课6的内容,使用AI(2025)可以参考以下方法: 四个洞见 模型已经比人聪明:以ChatGPT o3为代表的AI非常强大,能运用高级理论解释道理、引用最新学术论文,生成对顶尖科学家都有用的…...
