文章目录

  • 1.观察者模式简介
  • 2.观察者模式结构
  • 3.观察者模式代码实例
    • 3.0.公共头文件
    • 3.1.观察者
      • 3.1.1.抽象观察者Observer
      • 3.1.2.具体观察者Player
    • 3.2.目标类
      • 3.2.1.抽象目标AllyCenter
      • 3.2.2.具体目标AllyCenterController
    • 循环包含
      • 错误示例
    • “前向声明什么时候不够、必须 #include 对方头?”
    • 3.3.客户端代码示例及效果
  • 4.观察者模式的应用
  • 5.总结

代码仓库

观察者模式非常常见,近年来逐渐流行的响应式编程就是观察者模式的应用之一。观察者模式的思想就是一个对象发生一个事件后,逐一通知监听着这个对象的监听者,监听者可以对这个事件马上做出响应。

生活中有很多观察者模式的例子,比如我们平时的开关灯。当我们打开灯的开关时,灯马上亮了;当我们关闭灯的开关时,灯马上熄了。这个过程中,灯就对我们控制开关的事件做出了响应,这就是一个最简单的一对一观察者模式。当某某公众号发表一篇文章,所有关注了公众号的读者立即收到了文章,这个过程中所有关注了公众号的微信客户端就对公众号发表文章的事件做出了响应,这就是一个典型的一对多观察者模式。 这表明“更新发布文章”并不是孤立的,而是与众多对象产生了关联。一个对象行为的改变,其相关联的对象都会得到通知,并自动产生对应的行为。这在软件设计模式中,即是观察者模式。

再举个例子,比如警察一直观察着张三的一举一动,只要张三有什么违法行为,警察马上行动,抓捕张三。

在这里插入图片描述

众所周知,张三坏事做尽,是一个老法外狂徒了,所以不止一个警察会盯着张三,也就是说一个被观察者可以有多个观察者。当被观察者有事件发生时,所有观察者都能收到通知并响应。观察者模式主要处理的是一种一对多的依赖关系。 它的定义如下:

观察者模式(Observer Pattern):定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

1.观察者模式简介

软件系统中的对象并不是孤立存在的,一个对象行为的改变可能会引起其他所关联的对象的状态或行为也发生改变,即“牵一发而动全身”。观察者模式建立了一种一对多的联动,一个对象改变时将自动通知其他对象,其他对象将作出反应。观察者模式中,发生改变的对象称为“观察目标”,被通知的对象称为“观察者”。一个观察目标可以有很多个观察者。

观察者模式定义如下:
观察者模式:定义对象之间的一种一对多的依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象都得到通知并被自动更新。

观察者模式又被称为发布-订阅模式(Publish-Subscribe)、模型-视图模式(Model-View)、源-监听器模式(Source-Listener)、从属者模式(Dependents)。

2.观察者模式结构

观察者模式由观察者和观察目标组成,为便于扩展,两个角色都设计了抽象层。观察者模式的UML图如下:
在这里插入图片描述
在这里插入图片描述
下述是观察者模式的典型实现:

#define __DEMO_H__
#ifdef __DEMO_H__using namespace std;
#include <list>// 抽象观察者
class Observer
{
public:virtual ~Observer() {}// 声明响应更新方法virtual void update() = 0;
};// 具体观察者
class ConcreteObserver : public Observer
{
public:// 实现响应更新方法void update(){// 具体操作}
};// 抽象目标
class Subject
{
public:virtual ~Subject() {}// 添加观察者void attach(Observer *obs){obsList.push_back(obs);}// 移除观察者void detach(Observer *obs){obsList.remove(obs);}// 声明通知方法virtual void notify() = 0;protected:// 观察者列表list<Observer *> obsList;
};// 具体目标
class ConcreteSubject : public Subject
{
public:// 实现通知方法void notify(){// 具体操作// 遍历通知观察者对象for (int i = 0; i < obsList.size(); i++){obsList[i]->update();}}
};// 客户端代码示例
int main()
{Subject *sub = new ConcreteSubject();Observer *obs = new ConcreteObserver();sub->attach(obs);sub->notify();delete sub;delete obs;return 0;
}
#endif

3.观察者模式代码实例

玩过和平精英这款游戏吗?四人组队绝地求生,当一个队友发现物资时,可以发消息“我这里有物资”,其余三个队友听到后可以去取物资;当一个队友遇到危险时,也可以发消息“救救我”,其余三个队友得到消息后便立马赶去营救。本例将用观察者模式来模拟这个过程。

本例的UML图如下:
在这里插入图片描述
本例中,抽象观察者是Observer,声明了发现物资或者需要求救时的呼叫的方法call(),具体观察者是Player,即玩家,Player实现了呼叫call()方法,并且还定义了取物资come()和支援队友help()的方法本例定义了AllyCenter作为抽象目标,它维护了一个玩家列表playerList,并且定义了加入战队和剔除玩家的方法。 具体目标是联盟中心控制器AllyCenterController,它实现了通知notify()方法,该方法将队友call的消息传达给玩家列表里的其余队友,并作出相应的响应。

3.0.公共头文件

通过一个枚举类型来定义两种消息类型,即发现物资和求助

#ifndef __COMMON_H__
#define __COMMON_H__enum INFO_TYPE{NONE,RESOURCE,HELP
};#endif //__COMMON_H__

3.1.观察者

3.1.1.抽象观察者Observer

class Observer {
public:virtual ~Observer() = default;virtual void call(INFO_TYPE, AllyCenter* ac) = 0;const std::string& getName() const { return name; }void setName(std::string n){ name = std::move(n); }protected:std::string name{"none"};
};

3.1.2.具体观察者Player

// final: Player 不能再有子类
class Player final : public Observer {
public:Player() = default;explicit Player(std::string n) { setName(std::move(n)); }void call(INFO_TYPE, AllyCenter* ac) override; // 这里只声明void help() const;void come() const;
};

3.2.目标类

3.2.1.抽象目标AllyCenter

// 前向声明
// 不做前向声明时,两边的头文件往往互相 #include,形成“包含环”。 由于编译是单向读取的
class Observer;
// class Player;// 抽象目标:联盟中心
class AllyCenter {
public:AllyCenter();virtual ~AllyCenter() {}// 声明通知方法virtual void notify(INFO_TYPE infoType, const std::string& name) = 0;// 加入玩家void join(Observer *player);// 移除玩家void remove(Observer *player);protected:// 玩家列表std::vector<Observer*> playerList;
};

3.2.2.具体目标AllyCenterController

// 具体目标
class AllyCenterController : public AllyCenter {
public:AllyCenterController();// 实现通知方法void notify(INFO_TYPE infoType, const std::string& name) override;
};

循环包含

“循环包含”(include cycle)是指:头文件彼此相互 #include(直接或间接 A→B→A)。
预处理展开时会卡在一个环里——虽然 include guard 会阻止“无限展开”,但副作用是某一边在需要完整类型时只看到了前半张脸,于是报“未知类型 / 不完全类型(incomplete type)”之类的编译错误。

错误示例

// A.h
#ifndef A_H
#define A_H
#include "B.h"                 // A 想用 B
struct A { B b; };             // ← 按值成员,必须要 B 的完整定义
#endif// B.h
#ifndef B_H
#define B_H
#include "A.h"                 // B 又包含 A
struct B { A a; };             // ← 也按值成员,需要 A 的完整定义
#endif

编译时流程大致是:编译器读 A.h → 进 B.h → 试图再进 A.h,但被 include guard 拦住,于是此时 B.h 里看不到 A 的完整定义,马上报错(反过来顺序也一样)。

“前向声明什么时候不够、必须 #include 对方头?”

核心在于**“不完全类型 vs 完全类型”**。class X; 只是告诉编译器“有个类型 X”,但看不到它的大小、布局和成员。凡是需要知道大小/布局/成员或能析构的场合,都必须拿到完整定义(所以要 #include “X.h”)。
在这里插入图片描述
在这里插入图片描述

3.3.客户端代码示例及效果

#include <cstdio>
#include <memory>
#include "Observer.h"
#include "AllyCenter.h"int main() {// 创建一个战队auto controller = std::make_unique<AllyCenterController>();// 创建4个玩家,并加入战队auto Jungle     = std::make_unique<Player>("Jungle");auto Single     = std::make_unique<Player>("Single");auto Jianmengtu = std::make_unique<Player>("Diego");auto SillyDog   = std::make_unique<Player>("Richard");controller->join(Jungle.get());controller->join(Single.get());controller->join(Jianmengtu.get());controller->join(SillyDog.get());std::puts("");Jungle->call(RESOURCE, controller.get());std::puts("");SillyDog->call(HELP, controller.get());std::puts("");#ifdef _WIN32system("pause");
#endifreturn 0; // 智能指针自动释放
}

上述代码运行结果如下图:
在这里插入图片描述

4.观察者模式的应用

观察者模式是一种使用频率非常高的设计模式,几乎无处不在。凡是涉及一对一、一对多的对象交互场景,都可以使用观察者会模式。比如购物车,浏览商品时,往购物车里添加一件商品,会引起UI多方面的变化(购物车里商品数量、对应商铺的显示、价格的显示等);各种编程语言的GUI事件处理的实现;所有的浏览器事件(mouseover,keypress等)都是使用观察者模式的例子。

5.总结

在这里插入图片描述

之后我会持续更新,如果喜欢我的文章,请记得一键三连哦,点赞关注收藏,你的每一个赞每一份关注每一次收藏都将是我前进路上的无限动力 !!!↖(▔▽▔)↗感谢支持!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/92924.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/92924.shtml
英文地址,请注明出处:http://en.pswp.cn/web/92924.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CA证书、SSL加速器、HTTPS、HTTP和域名之间的关系

理解CA证书、SSL加速器、HTTPS、HTTP和域名之间的关系对于构建安全、高效的网站至关重要。它们共同构成了现代安全网络通信的基础。下面是它们各自的概念以及它们之间的关系&#xff1a;域名 概念&#xff1a; 人类可读的网站地址&#xff08;如 www.example.com&#xff09;。…

3D感知多模态(图像、雷达感知)

一.BEVFusion1.简要介绍BEV是一个俯视空间&#xff0c;Fusion做的就是融合&#xff0c;这里指的就是图像和点云的融合。那如何把图像和点云融合在一起&#xff1f;认为融合方法有三种&#xff1a;a.point level fusion:点集的融合&#xff0c;从点云中采样一些点,再根据相机的内…

STM32 HAL库驱动W25QXX Flash

STM32 HAL库驱动W25QXX Flash 1. 概述 W25QXX系列是一种SPI接口的Flash存储器&#xff0c;广泛应用于嵌入式系统中作为数据存储设备。本文档详细介绍了基于STM32 HAL库的W25QXX Flash驱动实现&#xff0c;包括硬件连接、驱动函数实现以及使用示例。 项目源码仓库&#xff1a…

Vivado自定义IP核学习笔记

文章目录【1】创建一个新的IP核【2】实现功能【3】编辑IP核【4】IP封装【5】创建Vivado工程【1】创建一个新的IP核 1.1 打开Vivado->点击【Tasks->Manage IP->New IP Location】->弹出窗口1.2 【Next】->设置IP属性->【Finish】->【OK】 【IP Location】…

【面试】高级开发面试场景题

1、如何保证MySql到ES的数据一致性? 答:ES是一个开元分布式搜索和分析引擎、它提供了全文搜索、结构化搜索分析以及这些组合的能力。 全文搜索能力:ES支持复杂的搜索能力,包括模糊匹配、短语查询、布尔查询等,并且可以快速的返回结果 实时数据分析:实时数据分析,支持对…

《 慢 SQL 分析与 SQL 优化实战指南》

&#x1f50d; 慢 SQL 分析与 SQL 优化实战指南、 &#x1f9e0;前言 在数据库性能调优中&#xff0c;慢 SQL 是性能瓶颈的常见元凶。 一次慢查询可能会拖垮整个业务线程池&#xff0c;甚至引发锁等待、雪崩效应。 对后端开发与 DBA 而言&#xff0c;快速定位并优化慢 SQL&am…

C#中如何运用JWT用户认证

一、JWT概述JSON Web Token&#xff08;JWT&#xff09;是一种轻量级的身份认证机制&#xff0c;广泛应用于分布式系统中的用户认证。它通过紧凑的JSON格式存储用户身份信息&#xff0c;并使用数字签名确保信息的完整性和真实性。与传统的基于Session的认证相比&#xff0c;JWT…

Hibernate 使用详解

在现代的Java开发中&#xff0c;数据持久化是一个至关重要的环节。而在众多持久化框架中&#xff0c;Hibernate以其强大的功能和灵活性&#xff0c;成为了开发者们的首选工具。本文将详细介绍Hibernate的原理、实现过程以及其使用方法&#xff0c;希望能为广大开发者提供一些有…

【图像算法 - 13】基于 YOLO12 与 OpenCV 的实时目标点击跟踪系统(系统介绍 + 源码详细)

基于 YOLO12 与 OpenCV 的实时点击目标跟踪系统 在计算机视觉领域&#xff0c;目标检测与跟踪是两个核心任务。本文将介绍一个结合 YOLO 目标检测模型与 OpenCV 跟踪算法的实时目标跟踪系统&#xff0c;该系统允许用户通过鼠标交互选择特定目标进行持续跟踪&#xff0c;支持多…

【数据库】 MySQL 表的操作详解

在 MySQL 数据库的日常开发与维护中&#xff0c;表的操作是最基础且最常用的部分。本文将从 创建表、查看表结构、修改表 以及 删除表 等方面进行详细讲解&#xff0c;并附上对应的 SQL 语句示例&#xff0c;方便在实际项目中直接应用。一、创建表 1.1 创建表语法 CREATE TABLE…

DiT: Transformer上的扩散模型

论文&#xff08;ICCV 2023&#xff09;&#xff1a;Scalable Diffusion Models with Transformers 代码和工程网页&#xff1a;https://www.wpeebles.com/DiT.html DiTs&#xff08;Diffusion Transformers&#xff09;是首个基于Transformer架构的扩散模型&#xff01;它在…

MySQL 索引:索引为什么使用 B+树?(详解B树、B+树)

文章目录一、二叉查找树(BST)&#xff1a;不平衡二、平衡二叉树(AVL)&#xff1a;旋转耗时三、红黑树&#xff1a;树太高由一个例子总结索引的特点基于哈希表实现的哈希索引高效的查找方式&#xff1a;二分查找基于二分查找思想的二叉查找树升级版的BST树&#xff1a;AVL 树四、…

ESP32入门开发·VScode空白项目搭建·点亮一颗LED灯

目录 1. 环境搭建 2. 创建项目 3. 调试相关介绍 4. 代码编写 4.1 包含头文件 4.2 引脚配置 4.3 设置输出电平 4.4 延时函数 4.5 调试 1. 环境搭建 默认已经搭建好环境&#xff0c;如果未搭建好可参考&#xff1a; ESP32入门开发Windows平台下开发环境的搭建…

ONLYOFFICE AI 智能体上线!与编辑器、新的 AI 提供商等进行智能交互

ONLYOFFICE AI 插件​迎来重要更新&#xff0c;带来了新功能和更智能的交互体验。随着 AI 智能体&#xff08;现为测试版&#xff09;的上线、带来更多 AI 提供商支持以及其他新功能&#xff0c;AI 插件已经成为功能强大的文档智能助理。 关于 ONLYOFFICE ONLYOFFICE 文档是多…

【C++进阶学习】第十一弹——C++11(上)——右值引用和移动语义

前言&#xff1a; 前面我们已经将C的重点语法讲的大差不差了&#xff0c;但是在C11版本之后&#xff0c;又出来了很多新的语法&#xff0c;其中有一些作用还是非常大的&#xff0c;今天我们就先来学习其中一个很重要的点——右值引用以及它所扩展的移动定义 目录 一、左值引用和…

【IoTDB】363万点/秒写入!IoTDB凭何领跑工业时序数据库赛道?

【作者主页】Francek Chen 【专栏介绍】⌈⌈⌈大数据与数据库应用⌋⌋⌋ 大数据是规模庞大、类型多样且增长迅速的数据集合&#xff0c;需特殊技术处理分析以挖掘价值。数据库作为数据管理的关键工具&#xff0c;具备高效存储、精准查询与安全维护能力。二者紧密结合&#xff0…

IEEE 2025 | 重磅开源!SLAM框架用“法向量+LRU缓存”,将三维重建效率飙升72%!

一、前言 当前研究领域在基于扩散模型的文本到图像生成技术方面取得了显著进展&#xff0c;尤其在视觉条件控制方面。然而&#xff0c;现有方法&#xff08;如ControlNet&#xff09;在组合多个视觉条件时存在明显不足&#xff0c;主要表现为独立控制分支在去噪过程中容易引入…

无人机遥控器教练模式技术要点

一、技术要点1.控制权仲裁机制&#xff1a;核心功能&#xff1a;清晰定义主控权归属逻辑&#xff08;默认为学员&#xff0c;但教练随时可接管&#xff09;。切换方式&#xff1a;通常通过教练遥控器上的物理开关&#xff08;瞬时或锁定型&#xff09;或软件按钮触发。切换逻辑…

【跨服务器的数据自动化下载--安装公钥,免密下载】

跨服务器的数据自动化下载功能介绍&#xff1a;上代码&#xff1a;发现好久没写csdn了&#xff0c;说多了都是泪~~ 以后会更新一些自动化工作的脚本or 小tricks&#xff0c;欢迎交流。分享一个最近在业务上写的较为实用的自动化脚本&#xff0c;可以批量从远端服务器下载指定数…

C++-->stl: list的使用

前言list的认识list是可以在固定时间&#xff08;O&#xff08;1&#xff09;&#xff09;内在任意位置进行插入和删除的序列式容器&#xff0c;并且该容器可以前后双向迭代。 2. list的底层是双向链表结构&#xff0c;双向链表中每个元素存储在互不相关的独立节点中&#xff0…