23种设计模式-观察者(Observer)设计模式
文章目录
- 一.什么是观察者模式?
- 二.观察者模式的结构
- 三.观察者模式的应用场景
- 四.观察者模式的优缺点
- 五.观察者模式的实现(C++示例)
- 六.观察者模式的实现(JAVA示例)
- 七.代码解释
- 八.总结
类图: 观察者设计模式类图
一.什么是观察者模式?
观察者模式(Observer Pattern)是一种行为型设计模式,用于定义对象之间的一对多关系。即一个对象(称为“主题”)状态发生改变时,会自动通知依赖它的其他对象(称为“观察者”)。这种模式在很多场景中都非常适用,比如发布-订阅系统、事件监听、界面刷新等。
观察者模式的特点是松耦合:观察者和主题不需要彼此了解细节,只需按照约定进行交互,这种设计提升了代码的可维护性和可扩展性。
二.观察者模式的结构
- Subject(主题):又称被观察者,用于存储和管理观察者,并在自身状态变化时通知观察者。
- Observer(观察者):观察者接口,用于定义更新方法,所有观察者实现该接口。
- ConcreteSubject(具体主题):继承自主题接口,实现状态管理和通知观察者的具体逻辑。
- ConcreteObserver(具体观察者):实现观察者接口,通过获取主题更新,执行相应的行为。
三.观察者模式的应用场景
- 事件监听:用户在UI界面点击按钮时,按钮组件通过通知事件监听器实现某种交互。
- 数据变化通知:在数据变化时,多个视图自动更新显示。例如,股票价格变化时,订阅者自动更新价格显示。
- 发布-订阅系统:发布者更新数据时,通知订阅的所有观察者,实现自动信息推送。
四.观察者模式的优缺点
-
优点:
- 解耦:主题与观察者之间的耦合度较低,可以独立扩展。
- 自动通知:当主题状态发生变化时,无需手动通知每个观察者。
-
缺点:
- 通知耗时:当观察者数量较多或更新频繁时,可能会导致性能问题。
- 顺序依赖:如果通知是同步进行的,通知的顺序会影响整个流程。
五.观察者模式的实现(C++示例)
#include <iostream>
#include <vector>
#include <string>// 观察者接口
class Observer {
public:virtual void update(const std::string &message) = 0;
};// 主题接口
class Subject {
public:virtual void addObserver(Observer* observer) = 0;virtual void removeObserver(Observer* observer) = 0;virtual void notifyObservers() = 0;
};// 具体主题类
class ConcreteSubject : public Subject {
private:std::vector<Observer*> observers;std::string message;public:void setMessage(const std::string &newMessage) {message = newMessage;notifyObservers(); // 通知所有观察者}void addObserver(Observer* observer) override {observers.push_back(observer);}void removeObserver(Observer* observer) override {observers.erase(std::remove(observers.begin(), observers.end(), observer), observers.end());}void notifyObservers() override {for (Observer* observer : observers) {observer->update(message);}}
};// 具体观察者类
class ConcreteObserver : public Observer {
private:std::string name;public:ConcreteObserver(const std::string &observerName) : name(observerName) {}void update(const std::string &message) override {std::cout << "Observer " << name << " received message: " << message << std::endl;}
};// 测试代码
int main() {ConcreteSubject subject;// 创建观察者ConcreteObserver observer1("Observer1");ConcreteObserver observer2("Observer2");// 添加观察者subject.addObserver(&observer1);subject.addObserver(&observer2);// 主题更新并通知观察者subject.setMessage("New message arrived");// 移除观察者并更新主题subject.removeObserver(&observer1);subject.setMessage("Another message");return 0;
}
六.观察者模式的实现(JAVA示例)
import java.util.ArrayList;
import java.util.List;// 抽象观察者类
interface Observer {void update(int state); // 更新方法
}// 具体观察者A
class ConcreteObserverA implements Observer {@Overridepublic void update(int state) {System.out.println("ConcreteObserverA: State updated to " + state);}
}// 具体观察者B
class ConcreteObserverB implements Observer {@Overridepublic void update(int state) {System.out.println("ConcreteObserverB: State updated to " + state);}
}// 抽象主题类
abstract class Subject {protected List<Observer> observers = new ArrayList<>();// 添加观察者public void attach(Observer observer) {observers.add(observer);}// 移除观察者public void detach(Observer observer) {observers.remove(observer);}// 通知所有观察者public void notifyObservers(int state) {for (Observer observer : observers) {observer.update(state);}}
}// 具体主题类
class ConcreteSubject extends Subject {private int state;public int getState() {return state;}public void setState(int state) {this.state = state;System.out.println("ConcreteSubject: State changed to " + state);notifyObservers(state); // 通知观察者}
}// 客户端代码
public class ObserverPatternDemo {public static void main(String[] args) {// 创建具体主题ConcreteSubject subject = new ConcreteSubject();// 创建观察者并订阅Observer observerA = new ConcreteObserverA();Observer observerB = new ConcreteObserverB();subject.attach(observerA);subject.attach(observerB);// 更改状态并通知观察者subject.setState(10);subject.setState(20);// 取消观察者A的订阅subject.detach(observerA);// 再次更改状态subject.setState(30);}
}
七.代码解释
- Observer 接口:Observer是一个抽象基类,包含update方法,所有观察者都要实现该方法,以响应主题的通知。
- Subject 接口:Subject也是一个抽象基类,提供addObserver、removeObserver和notifyObservers方法,主题通过这些方法管理和通知观察者。
- ConcreteSubject 类:这是主题的具体实现类,包含observers向量用于保存观察者。当主题状态改变时,通过调用notifyObservers通知所有观察者。
- ConcreteObserver 类:这是观察者的具体实现类。每个观察者都有一个name属性,并在update方法中响应通知。
- 测试代码:在main函数中创建了一个主题对象subject,并注册两个观察者对象observer1和observer2。主题更新时,所有观察者都会收到通知。
八.总结
观察者模式是一种非常实用的设计模式,特别是在需要通知多个对象的场景中非常合适。它让主题和观察者之间实现了低耦合,从而增强系统的灵活性和扩展性。通过实现一个通用的观察者接口和主题接口,观察者模式能够很好地应对需求的变化,使得观察者可以随时添加、删除,并根据主题的状态变化而自动更新,从而达到良好的解耦效果。
相关文章:

23种设计模式-观察者(Observer)设计模式
文章目录 一.什么是观察者模式?二.观察者模式的结构三.观察者模式的应用场景四.观察者模式的优缺点五.观察者模式的实现(C示例)六.观察者模式的实现(JAVA示例)七.代码解释八.总结 类图: 观察者设计模式类图…...

【CUDA】Branch Divergence and Unrolling Loop
目录 一、避免分支发散 1.1 并行规约问题 1.2 并行规约中的发散 二、UNrolling Loops 一、避免分支发散 控制流有时依赖于 thread 索引。同一个warp中,一个条件分支可能导致性能很差。通过重新组织数据获取模式可以减少或避免 warp divergence。具体问题查看下…...
深度学习:卷积神经网络的计算复杂度,顺序操作,最大路径长度
卷积层的计算复杂度 在深度学习中,卷积层的计算复杂度主要取决于卷积核的大小、输入和输出的通道数量、以及输入序列的长度。具体来说,卷积层的计算复杂度可以通过以下几个因素来计算: 卷积核大小 k:卷积核的大小决定了每次卷积操…...
springboot 配置文件中 multipart.max-file-size 各个版本的写法
由于springboot具有几个版本,不同版本对于文件上传最大限制的配置也有所不同。 所以要注意springboot本身的版本,不然会一直报错 在springboot1.3版本中: multipart.maxFileSize在springboot1.4与springboot1.5版本中: spring…...

linux 中mysql查看慢日志
1、到mysql容器,先登录到数据库,查看是否开启 mysql -h 127.0.0.1 -uroot -p SHOW VARIABLES LIKE slow_query_log; 2、如果没有开启,需要先开启 set global slow_query_log ON; 3、查看慢日志文件 SHOW VARIABLES LIKE slow_query_log…...
单片机的基本组成与工作原理
单片机(Microcontroller Unit, MCU)是一种将计算机的主要部分集成在一个芯片上的小型计算机系统。它通常包括中央处理器(CPU)、存储器(Memory)、输入输出接口(I/O Ports)、定时器/计…...
智慧隧道和智慧交通
通过引入先进的物联网技术,将各种硬件设备如传感器、摄像头、控制系统等有效地连接并管理起来,以实现道路安全和交通流畅的目标。这些设备将能够实时监控和控制隧道内的各种设备和系统,从而提高道路安全、提升驾驶体验并降低管理成本。 在这个…...
List、Set、Map详解和区别
在 Java 中,List、Set、Map是常用的集合类型,它们各自具有不同的特点和用途,以下是对它们的详细介绍及区别分析: List(列表) 特点: 有序性:List中的元素是有序的,即元素…...

界面控件DevExpress WinForms v24.2新功能预览 - 支持.NET 9
DevExpress WinForms 拥有180组件和UI库,能为Windows Forms平台创建具有影响力的业务解决方案。DevExpress WinForms能完美构建流畅、美观且易于使用的应用程序,无论是Office风格的界面,还是分析处理大批量的业务数据,它都能轻松胜…...

Postman之pm.test断言操作
Postman之pm.test断言操作 1.断言方法2.连接符3.条件判断符 用于验证请求的响应数据是否符合预期 1.断言方法 pm.test():定义一个测试函数,接受两个参数,一个字符串参数用来描述该测试,一个返回True/False的函数 语法格式&#…...
对数几率回归
对数几率回归简介 对数几率回归(Logistic Regression)是一种用于解决分类问题的经典统计模型,其核心思想是利用逻辑函数(Sigmoid函数)将线性回归模型的输出值映射到概率范围 [0, 1],从而实现分类预测。对数…...

docker 配置同宿主机共同网段的IP 同时通过通网段的另一个电脑实现远程连接docker
docker配置网络 #宿主机执行命令 ifconfig 查询对应的主机ip 子网掩码 网关地址 #[网卡名称]:inet[主机IP] netmask[子网掩码] broadcast[网关地址]这里需要重点关注:eno1[网卡名称]以及【192.168.31.225】网关地址 在宿主机执行docker命令创建一个虚拟…...
4-7-1.C# 数据容器 - LinkedList(LinkedList 的定义、LinkedList 结点的遍历、LinkedList 的常用方法)
LinkedList 概述 LinkedList<T> 通过节点(Node)来存储数据,每个节点包含数据和指向下一个节点的引用 LinkedList<T> 存储的元素是可重复的 LinkedList<T> 支持泛型,可以指定存储的元素的类型 LinkedList<…...

「三」体验HarmonyOS端云一体化开发模板——使用DevEco Studio直接创建端云一体化工程
关于作者 白晓明 宁夏图尔科技有限公司董事长兼CEO、坚果派联合创始人 华为HDE、润和软件HiHope社区专家、鸿蒙KOL、仓颉KOL 华为开发者学堂/51CTO学堂/CSDN学堂认证讲师 开放原子开源基金会2023开源贡献之星 「目录」 「一」HarmonyOS端云一体化概要 「二」体验HarmonyOS端云一…...

确保以管理员权限运行 Visual Studio 开发者命令提示符
文章目录 解决方法:1. 以管理员身份运行命令提示符2. 改变目录权限3. 改变项目目录位置4. 检查文件系统权限 总结: ********************************************************************** ** Visual Studio 2022 Developer Command Prompt v17.12.0 …...

命令执行简单(棱角社区有毒)
前言:小迪安全2022第一节反弹shell,小迪用的是两台都是云服务器,没有服务器可以在自己的主机上搭建也是可以的,主机上搭两个网站 思路:生成一个木马文件,下载到本机,然后利用本机上传到目标主机…...

Keil基于ARM Compiler 5的工程迁移为ARM Compiler 6的工程
环境: keil版本为5.38,版本务必高于5.30 STM32F4的pack包版本要高于2.9 软件包下载地址:https://zhuanlan.zhihu.com/p/262507061 一、更改Keil中编译器 更改后编译,会报很多错,先不管。 二、更改头文件依赖 观察…...
Kafka-创建topic源码
一、命令创建topic kafka-topics --create --topic quickstart-events --bootstrap-server cdh1:9092 --partitions 2 --replication-factor 2 二、kafka-topics脚本 exec $(dirname $0)/kafka-run-class.sh org.apache.kafka.tools.TopicCommand "$" 脚本中指定了…...

【网络安全】(一) 0成本添加访问级监控
互联网的安全感这个概念源于阿里。顾名思义,让互联网的用户对于web产品能够产生足够的信任和依赖。特别是涉及到用户资金交易的站点,一次严重的用户资料泄露就可以彻底毁掉你的品牌。 然而当前阶段除了bat大部分互联网行业的企业对于网络安全给的重视都…...

【Three.js基础学习】26. Animated galaxy
前言 shaders实现星系 课程回顾 使用顶点着色器为每个粒子设置动画 a属性 , u制服 ,v变化 像素比:window.devicePixelRatio 自动从渲染器检索像素比 renderer.getPixelRatio() 如何尺寸衰减, 放大缩小视角时,粒子都是同…...
【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密
在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...
大语言模型如何处理长文本?常用文本分割技术详解
为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...
MySQL用户和授权
开放MySQL白名单 可以通过iptables-save命令确认对应客户端ip是否可以访问MySQL服务: test: # iptables-save | grep 3306 -A mp_srv_whitelist -s 172.16.14.102/32 -p tcp -m tcp --dport 3306 -j ACCEPT -A mp_srv_whitelist -s 172.16.4.16/32 -p tcp -m tcp -…...
IP如何挑?2025年海外专线IP如何购买?
你花了时间和预算买了IP,结果IP质量不佳,项目效率低下不说,还可能带来莫名的网络问题,是不是太闹心了?尤其是在面对海外专线IP时,到底怎么才能买到适合自己的呢?所以,挑IP绝对是个技…...

算法:模拟
1.替换所有的问号 1576. 替换所有的问号 - 力扣(LeetCode) 遍历字符串:通过外层循环逐一检查每个字符。遇到 ? 时处理: 内层循环遍历小写字母(a 到 z)。对每个字母检查是否满足: 与…...
ThreadLocal 源码
ThreadLocal 源码 此类提供线程局部变量。这些变量不同于它们的普通对应物,因为每个访问一个线程局部变量的线程(通过其 get 或 set 方法)都有自己独立初始化的变量副本。ThreadLocal 实例通常是类中的私有静态字段,这些类希望将…...
Vue 实例的数据对象详解
Vue 实例的数据对象详解 在 Vue 中,数据对象是响应式系统的核心,也是组件状态的载体。理解数据对象的原理和使用方式是成为 Vue 专家的关键一步。我将从多个维度深入剖析 Vue 实例的数据对象。 一、数据对象的定义方式 1. Options API 中的定义 在 Options API 中,使用 …...
初级程序员入门指南
初级程序员入门指南 在数字化浪潮中,编程已然成为极具价值的技能。对于渴望踏入程序员行列的新手而言,明晰入门路径与必备知识是开启征程的关键。本文将为初级程序员提供全面的入门指引。 一、明确学习方向 (一)编程语言抉择 编…...

数据可视化交互
目录 【实验目的】 【实验原理】 【实验环境】 【实验步骤】 一、安装 pyecharts 二、下载数据 三、实验任务 实验 1:AQI 横向对比条形图 代码说明: 运行结果: 实验 2:AQI 等级分布饼图 实验 3:多城市 AQI…...

5. TypeScript 类型缩小
在 TypeScript 中,类型缩小(Narrowing)是指根据特定条件将变量的类型细化为更具体的过程。它帮助开发者编写更精确、更准确的代码,确保变量在运行时只以符合其类型的方式进行处理。 一、instanceof 缩小类型 TypeScript 中的 in…...