设计模式(4)--对象行为(7)--观察者
1. 意图
定义对象间的一种一对多的依赖关系,
当一个对象的状态改变时,所有依赖于它的对象都得到通知并被自动更新。
2. 四种角色
抽象目标(Subject)、具体目标(Concrete Subject)、抽象观察者(Observer)、
具体观察者(Concrete Observer)
3. 优点
3.1 目标和观察者之间的耦合是抽象的。
3.2 支持广播通信。
4. 缺点
4.1 可能导致意外的更新。
5. 相关模式
当目标和观察者间的依赖关系特别复杂时,需要一个维护这些关系的对象,
这样的对象称为ChangeMananger。
5.1 ChangeManager可使用单例模式来保证它是唯一的并且是可全局访问的。
5.2 ChangeManager充当目标和观察者之间的Mediator
6. 代码示意(C++)
#pragma once
#include <string>
#include <iostream>
#include <vector>
using namespace std;class Subject;class Observer
{
public:virtual void Update(Subject* pSubject) = 0;
protected:Observer() {}
};
class ConcreteObserver :public Observer
{string m_state;string m_name;
public:ConcreteObserver(const string& name) :m_name(name) {}virtual void Update(Subject* pSubject);
};class Subject
{vector<Observer*> m_observers;
public:virtual string GetState() = 0;virtual void SetState(const string& state) = 0;
public:void Attach(Observer* pObserver) {m_observers.emplace_back(pObserver);cout << "After attached, observers size is:" << m_observers.size() << endl;}void Detach(Observer* pObserver) {m_observers.erase(std::remove_if(m_observers.begin(), m_observers.end(), [&](Observer* p) { return p == pObserver; }), m_observers.end());cout << "After detached, observers size is:" << m_observers.size() << endl;}void Notify(){auto it = m_observers.begin();while (it != m_observers.end()) {(*it)->Update(this);++it;}}
protected:Subject(){}
};
class ConcreteSubject :public Subject
{string m_state;
public:virtual string GetState() {return m_state;}virtual void SetState(const string& state) {m_state = state;}
};
Observer.cpp:
#include "Observer.h"void ConcreteObserver::Update(Subject* pSubject) {m_state = pSubject->GetState();cout << "Observer:" << m_name << ",got state from subject:" << m_state << endl;
}
#include "Observer.h"
int main() {Subject* pSubject = new ConcreteSubject();Observer* pObserver1 = new ConcreteObserver("obs1");Observer* pObserver2 = new ConcreteObserver("obs2");pSubject->Attach(pObserver1);pSubject->Attach(pObserver2);pSubject->SetState("hello1");pSubject->Notify();pSubject->Detach(pObserver1);pSubject->SetState("hello2");pSubject->Notify();delete pObserver2;delete pObserver1;delete pSubject;return 0;
}
运行结果:
6.1 目标和观察者之间只知道彼此的抽象类(3.1)。
6.2 Subject::Notify里的循环就是广播,观察者自己决定是否处理某一通知(3.2)。
6.3 使用ChangeMananger会使代码复杂些,但简化了Subject,且使更新策略更加灵活。
使用ChangeMananger代码示意:
#pragma once
#include <string>
#include <iostream>
#include <vector>
#include <map>
using namespace std;class Subject;class Observer
{
public:virtual void Update(Subject* pSubject) = 0;virtual string GetName() = 0;
protected:Observer() {}
};
class ConcreteObserver :public Observer
{string m_state;string m_name;
public:ConcreteObserver(const string& name) :m_name(name) {}virtual string GetName() { return m_name; }virtual void Update(Subject* pSubject);
};class ChangeManager;
class SimpleChangeManager;class Subject
{ChangeManager* m_pChangeManager;
public:virtual string GetState() = 0;virtual void SetState(const string& state) = 0;virtual ~Subject();
public:void Attach(Observer* pObserver);void Detach(Observer* pObserver);void Notify();
protected:Subject();
};
class ConcreteSubject :public Subject
{string m_state;
public:ConcreteSubject() {}virtual string GetState() {return m_state;}virtual void SetState(const string& state) {m_state = state;}
};class ChangeManager
{
public:virtual void Register(Subject* pSubject, Observer* pObserver) = 0;virtual void Unregister(Subject* pSubject, Observer* pObserver) = 0;virtual void Notify() = 0;
protected:ChangeManager() {}
};class SimpleChangeManager :public ChangeManager
{map<Subject*, vector<Observer*> > m_mapSubjects;
private:static SimpleChangeManager* s_instance;
public:static ChangeManager* Instance() {if (0 == s_instance) {s_instance = new SimpleChangeManager();}return s_instance;}static void DelInstance() {delete s_instance;s_instance = 0;}
public:virtual void Register(Subject* pSubject, Observer* pObserver) {vector<Observer*>& observers = m_mapSubjects[pSubject];auto it = find(observers.begin(), observers.end(), pObserver);if (it == observers.end()) {observers.emplace_back(pObserver);cout << pObserver->GetName() << " is registered successful" << endl;}else {cout << pObserver->GetName() << " is already registered" << endl;}}virtual void Unregister(Subject* pSubject, Observer* pObserver) {auto it = m_mapSubjects.find(pSubject);if (it == m_mapSubjects.end()) {cout << "No need unregister in map for:" << pObserver->GetName() << endl;}else {vector<Observer*>& observers = m_mapSubjects[pSubject];auto itRemove = remove_if(observers.begin(), observers.end(), [&](Observer* p) { return p == pObserver; });if (itRemove == observers.end()) {cout << "No need unregister in vector for:" << pObserver->GetName() << endl;}else {observers.erase(itRemove, observers.end());cout << pObserver->GetName() << " is unregistered successful" << endl;if (observers.size() == 0) {m_mapSubjects.erase(pSubject);}}}}virtual void Notify() {for (auto& pair : m_mapSubjects) {vector<Observer*>& observers = pair.second;auto it = observers.begin();while (it != observers.end()) {(*it)->Update(pair.first);++it;}}}
protected:SimpleChangeManager() {}
};
Observer.cpp:
#include "Observer.h"void ConcreteObserver::Update(Subject* pSubject) {m_state = pSubject->GetState();cout << "Observer:" << m_name << ",got state from subject:" << m_state << endl;
}Subject::Subject() {m_pChangeManager = SimpleChangeManager::Instance();
}
Subject::~Subject() {m_pChangeManager = 0;SimpleChangeManager::DelInstance();
}
void Subject::Attach(Observer* pObserver) {m_pChangeManager->Register(this, pObserver);
}
void Subject::Detach(Observer* pObserver) {m_pChangeManager->Unregister(this, pObserver);
}
void Subject::Notify()
{m_pChangeManager->Notify();
}SimpleChangeManager* SimpleChangeManager::s_instance = 0;
#include "Observer.h"
int main() {Subject* pSubject = new ConcreteSubject();Observer* pObserver1 = new ConcreteObserver("obs1");Observer* pObserver2 = new ConcreteObserver("obs2");pSubject->Attach(pObserver1);pSubject->Attach(pObserver2);pSubject->Attach(pObserver1);pSubject->SetState("hello1");pSubject->Notify();pSubject->Detach(pObserver1);pSubject->Detach(pObserver1);pSubject->SetState("hello2");pSubject->Notify();pSubject->Detach(pObserver2);pSubject->Detach(pObserver2);delete pObserver2;delete pObserver1;delete pSubject;return 0;
}
运行结果:
相关文章:

设计模式(4)--对象行为(7)--观察者
1. 意图 定义对象间的一种一对多的依赖关系, 当一个对象的状态改变时,所有依赖于它的对象都得到通知并被自动更新。 2. 四种角色 抽象目标(Subject)、具体目标(Concrete Subject)、抽象观察者(Observer)、 具体观察者(Concrete Observer) 3. 优点 3.1 …...

MySQL所有常见问题
一、事务 定义:一组操作要么全部成功,要么全部失败,目的是为了保证数据最终的一致性 在MySQL中,提供了一系列事务相关的命令: start transaction | begin | begin work:开启一个事务commit:提交一个事务rollback:回滚一个事务事务的ACID 原子性(Atomicity):当前事…...
锐捷交换机配置 SNMP
配置步骤 ( SNMP v2 ) 步骤一 -- 创建共同体(Community) ruijie(config)#snmp-server community test rw # rw 为读和写口令ruijie(config)#snmp-server community public ro # ro 为只读和写口令这里的共同体为“test”,通常只读口令和读写口令单独配置,提升安…...

Windows 10 安装和开启VNCServer 服务
Windows 10 安装和开启VNCServer 服务 登录云服务器 使用本地RDP登录到配置VNCServer服务的Windows10系统的云服务器。 下载VNC Server安装包 打开官网下载VNCServer安装包 URL:https://www.realvnc.com/en/connect/download/vnc/windows/ 安装VNC Server 双击…...

js遍历后端返回的集合将条件相同的放入同一个数组内
项目场景: echarts折线图需要根据条件动态展示多条不同曲线 解决方案: 后端直接将使用sql将数据查询出来返回即可,因为我这里不是Java使用的C#不是很熟练后台不好写逻辑,所以在前端js完成的 代码如下: function createline(villagename, buildingname…...

GcExcel:DsExcel 7.0 for Java Crack
GcExcel:DsExcel 7.0-高速 Java Excel 电子表格 API 库 Document Solutions for Excel(DsExcel,以前称为 GcExcel)Java 版允许您在 Java 应用程序中以编程方式创建、编辑、导入和导出 Excel 电子表格。几乎可以部署在任何地方。 创建、加载、…...
基于SpringBoot的职业生涯规划系统
文章目录 项目介绍主要功能截图:部分代码展示设计总结项目获取方式🍅 作者主页:超级无敌暴龙战士塔塔开 🍅 简介:Java领域优质创作者🏆、 简历模板、学习资料、面试题库【关注我,都给你】 🍅文末获取源码联系🍅 项目介绍 基于SpringBoot的职业生涯规划系统,java…...

基于Java+SpringBoot+vue+elementui的校园文具商城系统详细设计和实现
基于JavaSpringBootvueelementui的校园文具商城系统详细设计和实现 欢迎点赞 收藏 ⭐留言 文末获取源码联系方式 文章目录 基于JavaSpringBootvueelementui的校园文具商城系统详细设计和实现前言介绍:系统设计:系统开发流程用户登录流程系统操作流程 功能…...
PyTorch中常用的工具(5)使用GPU加速:CUDA
文章目录 前言4 使用GPU加速:CUDA5 小结 前言 在训练神经网络的过程中需要用到很多的工具,最重要的是数据处理、可视化和GPU加速。本章主要介绍PyTorch在这些方面常用的工具模块,合理使用这些工具可以极大地提高编程效率。 由于内容较多&am…...
Qt+opencv 视频分解为图片
最近遇到一些售前提供的BUG,但是他们提供的是录像视频,因为处理显示速度比较快,因此很难找到出现问题的位置。需要反复播放,自己编写了一个视频分解成图片这样就可以一张图一张图的对比,方便查看。 开发环境 qtopenv…...
一篇文章认识微服务的优缺点和微服务技术栈
目录 1、微服务 2、微服务架构 3、微服务优缺点 3.1 优点 3.2 缺点 4、微服务技术栈 1、微服务 微服务化的核心就是将传统的一站式应用,根据业务拆分成一个一个的服务,彻底地去耦合,每一个微服务提供单个业务功能的服务,一…...
[spark] dataframe的数据导入Mysql5.6
在 Spark 项目中使用 Scala 连接 MySQL 5.6 并将 DataFrame 中的数据保存到 MySQL 中的步骤如下: 添加 MySQL 连接驱动依赖: 在 Spark 项目中,你需要在项目的构建工具中添加 MySQL 连接驱动的依赖。 如果使用 Maven,可以在 pom.xm…...

2023年度业务风险报告:四个新风险趋势
目录 倒票的黄牛愈加疯狂 暴增的恶意网络爬虫 愈加猖獗的羊毛党 层出不穷的新风险 业务风险呈现四个趋势 防御云业务安全情报中心“2023年业务风险数据”统计显示,恶意爬虫风险最多,占总数的37.8%;其次是虚假账号注册,占18.79%&am…...

python编程从入门到实践(1)
文章目录 2.2.1命名的说明2.3字符串2.3.1使用方法修改字符串的大小写2.3.2 在字符串中使用变量2.3.3 制表符 和 换行符2.5.4删除空白2.5.5 删除前缀+后缀 2.2.1命名的说明 只能包含:字母,下划线,数字 必须:字母&#…...

ElasticSearch 文档操作
创建文档 指定id // 无则插入,有则覆盖(覆盖的逻辑是先删除,再插入) PUT /<target>/_doc/<_id> // 无则插入,有则覆盖 POST /<target>/_doc/<_id> // 无则插入,有则报错 PUT /&l…...

NXOpenC++布尔求和命令
一、概述 在进行批量布尔求和时,采用NXOpenC的方式要比UFun的方式美观的多,个人认为,ufun中UF_MODL_unite_bodies函数采用的是两两进行合并,显示多个步骤,而NXOpenC采用的是一个工具体和多个目标体进行合并,…...

ubuntu python播放MP3,wav音频和录音
目录 一.利用pygame(略显麻烦,有时候播放不太正常)1.安装依赖库2.代码 二.利用mpg123(简洁方便,但仅争对mp3)1.安装依赖库2.代码 三.利用sox(简单方便,支持的文件格式多)…...

Rust学习笔记000 安装
安装命令 curl --proto https --tlsv1.2 -sSf https://sh.rustup.rs | sh $ curl --proto https --tlsv1.2 -sSf https://sh.rustup.rs | sh info: downloading installerWelcome to Rust!This will download and install the official compiler for the Rust programming la…...
python AI五子棋对战
我写过一篇c++五子棋 c++五子棋代码-CSDN博客 现在又写了python import copy import time from enum import IntEnum import pygame from pygame.locals import *time = time.strftime("%Y-%m-%d %H:%M:%S") version = str(time)# 基础参数设置 square_size = 40 …...

图文证明 费马,罗尔,拉格朗日,柯西
图文证明 罗尔,拉格朗日,柯西 费马引理和罗尔都比较好证,不过多阐述,看图即可: 费马引理: 罗尔定理: 重点来证明拉格朗日和柯西 拉格朗日: 我认为不需要去看l(x)的那一行更好推: 详细的推理过程: 构造 h ( x ) f ( x ) − l ( x ) , 因为 a , b 两点为交点 , f ( a ) l ( …...
逻辑回归:给不确定性划界的分类大师
想象你是一名医生。面对患者的检查报告(肿瘤大小、血液指标),你需要做出一个**决定性判断**:恶性还是良性?这种“非黑即白”的抉择,正是**逻辑回归(Logistic Regression)** 的战场&a…...

通过Wrangler CLI在worker中创建数据库和表
官方使用文档:Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后,会在本地和远程创建数据库: npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库: 现在,您的Cloudfla…...
CMake控制VS2022项目文件分组
我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...

蓝桥杯3498 01串的熵
问题描述 对于一个长度为 23333333的 01 串, 如果其信息熵为 11625907.5798, 且 0 出现次数比 1 少, 那么这个 01 串中 0 出现了多少次? #include<iostream> #include<cmath> using namespace std;int n 23333333;int main() {//枚举 0 出现的次数//因…...

python执行测试用例,allure报乱码且未成功生成报告
allure执行测试用例时显示乱码:‘allure’ �����ڲ����ⲿ���Ҳ���ǿ�&am…...

【笔记】WSL 中 Rust 安装与测试完整记录
#工作记录 WSL 中 Rust 安装与测试完整记录 1. 运行环境 系统:Ubuntu 24.04 LTS (WSL2)架构:x86_64 (GNU/Linux)Rust 版本:rustc 1.87.0 (2025-05-09)Cargo 版本:cargo 1.87.0 (2025-05-06) 2. 安装 Rust 2.1 使用 Rust 官方安…...

【C++特殊工具与技术】优化内存分配(一):C++中的内存分配
目录 一、C 内存的基本概念 1.1 内存的物理与逻辑结构 1.2 C 程序的内存区域划分 二、栈内存分配 2.1 栈内存的特点 2.2 栈内存分配示例 三、堆内存分配 3.1 new和delete操作符 4.2 内存泄漏与悬空指针问题 4.3 new和delete的重载 四、智能指针…...

从物理机到云原生:全面解析计算虚拟化技术的演进与应用
前言:我的虚拟化技术探索之旅 我最早接触"虚拟机"的概念是从Java开始的——JVM(Java Virtual Machine)让"一次编写,到处运行"成为可能。这个软件层面的虚拟化让我着迷,但直到后来接触VMware和Doc…...

Linux操作系统共享Windows操作系统的文件
目录 一、共享文件 二、挂载 一、共享文件 点击虚拟机选项-设置 点击选项,设置文件夹共享为总是启用,点击添加,可添加需要共享的文件夹 查询是否共享成功 ls /mnt/hgfs 如果显示Download(这是我共享的文件夹)&…...

spring boot使用HttpServletResponse实现sse后端流式输出消息
1.以前只是看过SSE的相关文章,没有具体实践,这次接入AI大模型使用到了流式输出,涉及到给前端流式返回,所以记录一下。 2.resp要设置为text/event-stream resp.setContentType("text/event-stream"); resp.setCharacter…...