1. 模式定义与核心思想
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。当这个主题对象的状态发生变化时,它会自动通知所有观察者对象,使它们能够自动更新自己。

核心思想: 解耦主题和观察者。主题不需要知道哪些具体对象在观察它,它只需要维护一个观察者列表,并在状态改变时向它们发送通知。这使得系统更灵活,可以动态地添加和删除观察者,而无需修改主题的代码。

2. 模式结构(角色分析)
观察者模式通常包含以下四个角色:

Subject (主题 / 被观察者):

维护一个观察者(Observer)对象的集合。

提供接口可以添加(Attach)和删除(Detach)观察者。

提供接口用于通知(Notify)所有注册的观察者。

ConcreteSubject (具体主题):

继承自 Subject。

维护其自身的状态(例如,一个温度值、一个鼠标点击位置)。

当状态发生改变时,调用父类的 Notify 方法通知所有观察者。

Observer (观察者):

为所有具体观察者定义一个更新接口。通常是一个抽象的 Update() 方法。

当接到主题的通知时,调用此方法做出响应。

ConcreteObserver (具体观察者):

继承自 Observer。

实现 Update() 方法。在此方法中,通常会从 ConcreteSubject 获取所需的状态,并执行具体的业务逻辑(如更新UI、记录日志等)。

通常会维护一个指向 ConcreteSubject 的引用,以便在更新时获取其状态。

它们之间的协作关系如下图所示:
(这是一个UML协作序列的文本描述)

ConcreteObserver 调用 ConcreteSubject 的 Attach 方法将自己注册到主题的观察者列表中。

ConcreteSubject 的内部状态发生变化。

ConcreteSubject 调用 Notify 方法。

Notify 方法遍历所有注册的 Observer,并调用每个观察者的 Update 方法。

ConcreteObserver 的 Update 方法被调用,它可以通过传入的参数或查询 ConcreteSubject 来获取新状态,并据此更新自身。

3. 经典C++实现示例
下面是一个简单的C++实现,模拟一个气象站(主题)和多个显示设备(观察者)。

#include <iostream>
#include <vector>
#include <string>
#include <memory>// 前向声明
class Subject;// 1. Observer (观察者接口)
class Observer {
public:virtual ~Observer() = default;// 更新接口,参数通常是Subject或状态数据virtual void Update(Subject& subject) = 0;
};// 2. Subject (主题接口)
class Subject {
public:virtual ~Subject() = default;// 注册观察者void Attach(std::shared_ptr<Observer> observer) {observers_.push_back(observer);}// 移除观察者void Detach(std::shared_ptr<Observer> observer) {// 在实际项目中,这里需要更安全的删除逻辑observers_.erase(std::remove(observers_.begin(), observers_.end(), observer),observers_.end());}// 通知所有观察者void Notify() {for (auto& observer : observers_) {observer->Update(*this);}}private:std::vector<std::shared_ptr<Observer>> observers_;
};// 3. ConcreteSubject (具体主题:气象站)
class WeatherStation : public Subject {
public:// 设置状态(温度)并通知观察者void SetTemperature(double temp) {temperature_ = temp;Notify(); // 关键一步:状态改变,立即通知所有观察者}// 获取状态(供观察者查询)double GetTemperature() const {return temperature_;}private:double temperature_ = 0.0;
};// 4. ConcreteObserver (具体观察者:手机显示)
class PhoneDisplay : public Observer {
public:explicit PhoneDisplay(const std::string& name) : name_(name) {}void Update(Subject& subject) override {// 安全的向下转型,确认主题类型WeatherStation* ws = dynamic_cast<WeatherStation*>(&subject);if (ws) {double temp = ws->GetTemperature();std::cout << "[" << name_ << "] Temperature updated: " << temp << "°C\n";}}private:std::string name_;
};// 5. 另一个具体观察者:LED大屏显示
class LedDisplay : public Observer {
public:void Update(Subject& subject) override {WeatherStation* ws = dynamic_cast<WeatherStation*>(&subject);if (ws) {double temp = ws->GetTemperature();std::cout << "*** LED Display: CURRENT TEMP = " << temp << "°C ***\n";}}
};// 客户端代码
int main() {// 创建主题(气象站)WeatherStation station;// 创建观察者(两个显示设备)auto phone1 = std::make_shared<PhoneDisplay>("User's Phone");auto phone2 = std::make_shared<PhoneDisplay>("Dad's Phone");auto led = std::make_shared<LedDisplay>();// 注册观察者station.Attach(phone1);station.Attach(phone2);station.Attach(led);// 模拟气象站温度变化,观察者会自动更新std::cout << "Setting temperature to 25.5°C...\n";station.SetTemperature(25.5);std::cout << "\nSetting temperature to 18.2°C...\n";station.SetTemperature(18.2);// 移除一个观察者std::cout << "\nDetaching Dad's Phone...\n";station.Detach(phone2);std::cout << "Setting temperature to 30.0°C...\n";station.SetTemperature(30.0);return 0;
}

输出结果:

Setting temperature to 25.5°C...
[User's Phone] Temperature updated: 25.5°C
[Dad's Phone] Temperature updated: 25.5°C
*** LED Display: CURRENT TEMP = 25.5°C ***Setting temperature to 18.2°C...
[User's Phone] Temperature updated: 18.2°C
[Dad's Phone] Temperature updated: 18.2°C
*** LED Display: CURRENT TEMP = 18.2°C ***Detaching Dad's Phone...
Setting temperature to 30.0°C...
[User's Phone] Temperature updated: 30°C
*** LED Display: CURRENT TEMP = 30°C ***

更多例子:

示例 1: GUI 事件处理 (按钮点击)

#include <iostream>
#include <vector>
#include <memory>
#include <algorithm>// 前向声明
class Button;// 观察者接口:事件监听器
class EventListener {
public:virtual ~EventListener() = default;virtual void onClick(Button& source) = 0;
};// 主题:按钮
class Button {std::vector<EventListener*> listeners_; // 使用原始指针简化示例std::string name_;public:Button(const std::string& name) : name_(name) {}// 注册观察者void addListener(EventListener* listener) {listeners_.push_back(listener);}// 移除观察者void removeListener(EventListener* listener) {listeners_.erase(std::remove(listeners_.begin(), listeners_.end(), listener), listeners_.end());}// 模拟用户点击void click() {std::cout << "Button '" << name_ << "' was clicked!\n";notifyListeners();}const std::string& getName() const { return name_; }private:// 通知所有观察者void notifyListeners() {for (auto listener : listeners_) {listener->onClick(*this);}}
};// 具体观察者:登录处理器
class LoginHandler : public EventListener {
public:void onClick(Button& source) override {std::cout << "[LoginHandler] Handling click from: " << source.getName() << "\n";std::cout << "Performing login logic...\n";}
};// 具体观察者:日志记录器
class ClickLogger : public EventListener {
public:void onClick(Button& source) override {std::cout << "[ClickLogger] Button clicked: " << source.getName() << " at timestamp: 12345\n";}
};int main() {Button loginButton("Login");LoginHandler loginHandler;ClickLogger logger;// 注册监听器loginButton.addListener(&loginHandler);loginButton.addListener(&logger);// 模拟点击事件loginButton.click();std::cout << "\nRemoving logger...\n";loginButton.removeListener(&logger);loginButton.click();return 0;
}

输出结果:

Button 'Login' was clicked!
[LoginHandler] Handling click from: Login
Performing login logic...
[ClickLogger] Button clicked: Login at timestamp: 12345Removing logger...
Button 'Login' was clicked!
[LoginHandler] Handling click from: Login
Performing login logic...

示例 2: 发布-订阅系统 (简单的消息主题)

#include <iostream>
#include <vector>
#include <string>
#include <map>
#include <algorithm>// 消息类
struct Message {std::string topic;std::string content;
};// 订阅者接口
class Subscriber {
public:virtual ~Subscriber() = default;virtual void onMessage(const Message& msg) = 0;
};// 消息代理(主题)
class MessageBroker {std::map<std::string, std::vector<Subscriber*>> topicSubscribers_;public:// 订阅主题void subscribe(const std::string& topic, Subscriber* sub) {topicSubscribers_[topic].push_back(sub);}// 取消订阅void unsubscribe(const std::string& topic, Subscriber* sub) {auto& subs = topicSubscribers_[topic];subs.erase(std::remove(subs.begin(), subs.end(), sub), subs.end());}// 发布消息void publish(const Message& msg) {auto it = topicSubscribers_.find(msg.topic);if (it != topicSubscribers_.end()) {for (auto sub : it->second) {sub->onMessage(msg);}}}
};// 具体订阅者:日志服务
class LogService : public Subscriber {
public:void onMessage(const Message& msg) override {std::cout << "[LogService] Received on topic '" << msg.topic << "': " << msg.content << "\n";}
};// 具体订阅者:告警服务
class AlertService : public Subscriber {
public:void onMessage(const Message& msg) override {if (msg.topic == "alerts") {std::cout << "[AlertService] ALERT! " << msg.content << "\n";}}
};int main() {MessageBroker broker;LogService logger;AlertService alerter;// 订阅主题broker.subscribe("logs", &logger);broker.subscribe("alerts", &logger);broker.subscribe("alerts", &alerter);// 发布消息broker.publish({"logs", "System started successfully"});broker.publish({"alerts", "CPU usage over 90%"});broker.publish({"metrics", "This will be ignored by all"}); // 无订阅者return 0;
}

输出结果:

[LogService] Received on topic 'logs': System started successfully
[LogService] Received on topic 'alerts': CPU usage over 90%
[AlertService] ALERT! CPU usage over 90%

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

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

相关文章

[系统架构设计师]论文(二十三)

[系统架构设计师]论文&#xff08;二十三&#xff09; 一.论软件系统架构评估 1.架构所关注的质量属性主要有&#xff1a;性能&#xff0c;可用性&#xff0c;安全性&#xff0c;可修改性 1&#xff09;性能。性能是指系统的响应能力&#xff0c;即要经过多长时间才能对某个事件…

攻克 Java 分布式难题:并发模型优化与分布式事务处理实战指南

攻克 Java 分布式难题&#xff1a;并发模型优化与分布式事务处理实战指南 开场&#xff1a;从“摇摇欲坠”到“稳如磐石”&#xff0c;你的分布式系统进阶之路 你是否曾经遇到过这样的场景&#xff1f;精心打造的电商应用&#xff0c;在大促开启的瞬间&#xff0c;页面响应变得…

如何在Ubuntu中删除或修改已有的IP地址设置?

在 Ubuntu 中为新增加的网卡设置网络时&#xff0c;需要区分原有网卡和新网卡的配置&#xff0c;确保它们可以独立工作&#xff08;可在同一网段或不同网段&#xff09;。以下是具体步骤&#xff0c;假设你需要为新网卡配置静态 IP&#xff08;以 192.168.1.190/24 为例&#x…

Ansible Playbook 概述与实践案例(下)

#作者&#xff1a;张桐瑞 文章目录四、条件判断的实现五、循环的实现六、Jinja模板应用1、Jinja模板2、handlers组件七、角色 role1、角色介绍2、案例: 部署zabbix-agent四、条件判断的实现 when: 条件 - hosts: appserveruser: roottasks:- name: create userAuser: nameuser…

LeetCode 100 -- Day6

1. 哈希&#xff1a;49、128&#xff08;1&#xff09;49 字母异位词分组 -- 字典from collections import defaultdict class Solution(object):def groupAnagrams(self, strs):"""创建字典{sorted_string&#xff1a;原str}"""resultsdefaultd…

多因素认证(MFA/2FA)实战指南:如何保护你的账号

一、MFA/2FA 基础认知 1. 概念辨析与演进 单因素认证&#xff08;1FA&#xff09;的局限性&#xff1a;仅依赖 “知识因素”&#xff08;如密码&#xff09;&#xff0c;据 2024 年 Verizon 数据泄露报告&#xff0c;81% 的账户入侵源于密码泄露 —— 要么是用户使用弱密码&a…

vue3 字符 居中显示

在Vue 3中&#xff0c;要实现字符的居中显示&#xff0c;你可以使用多种方法&#xff0c;具体取决于你是想在HTML元素内居中文本&#xff0c;还是在CSS样式中实现。下面是一些常见的方法&#xff1a;1. 使用内联样式你可以直接在元素上使用style属性来实现文本的居中。<temp…

《Spring Boot 进阶:从零到一打造自定义 @Transactional》 ——支持多数据源、动态传播行为、可插拔回滚策略

《Spring Boot 进阶&#xff1a;从零到一打造自定义 Transactional》 ——支持多数据源、动态传播行为、可插拔回滚策略版本&#xff1a;Spring Boot 3.2.x JDK 17一、背景与痛点痛点默认 Transactional 限制多数据源只能绑定一个 DataSourceTransactionManager多租户无法在运…

open3D学习笔记

这里写自定义目录标题 核心3D数据结构 1.1 PointCloud(点云) 最近邻搜索 (KNN/Radius) 与空间索引(KDTree/Octree) 法线估计 (Normal Estimation) 聚类分割 (基于欧氏距离的聚类) 1.2 TriangleMesh (三角形网格) 泊松表面重建 (Poisson Surface Reconstruction) 滚球法 (Ba…

gt_k_char设计模块

是不是再fiber或者gt设计中经常遇到接收数据没有对齐&#xff1f;是的。很多协议需要手动对齐设计。这不&#xff0c;它来了。下面是手动对齐代码设计&#xff0c;本人在很多工程和项目中应用过&#xff0c;现在共享出来&#xff0c;给大家使用。module gt_k_char (input …

网页版云手机怎么样

随着科技的不断发展&#xff0c;云手机这一新兴概念逐渐走入大众视野&#xff0c;而网页版云手机作为云手机的一种便捷使用方式&#xff0c;备受关注&#xff0c;下面从多个方面来探讨网页版云手机究竟怎么样。与传统的需要在本地设备安装专门APP的云手机使用方式不同&#xff…

XFile v2 系统架构文档

XFile v2 系统架构文档 1. 概述 XFile 是一个基于 Go 语言开发的分布式文件管理系统&#xff0c;提供本地文件存储、网络文件共享、安全认证和多种文件操作功能。该系统采用模块化设计&#xff0c;支持大文件分片存储、用户权限管理、双因素认证等高级功能。 XFile系统的核心特…

写一个天气查询Mcp Server

上篇文章&#xff0c;我们聊到了 MCP 的基本概念&#xff0c;带大家快速入门了 MCP。 说入门应该毫不夸张&#xff0c;对于科普性质的文章&#xff0c;只需要知道这件事情的诞生背景以及有什么作用就可以了。 但是&#xff0c;如果要开发给大模型调用的 Mcp Server&#xff0…

leecode-三数之和

思路 我的思路先顺序遍历一个变量,然后使用首尾双指针去遍历&#xff0c;根据结果去更新另外两个变量&#xff0c;如何和为零&#xff0c;将结果加入集合&#xff0c;但是这里要注意去重。 class Solution {public List<List<Integer>> threeSum(int[] nums) {// 排…

【数学建模】灰色关联分析的核心步骤

文章目录步骤一&#xff1a;读数据步骤二&#xff1a;指标正向化步骤三&#xff1a;数据标准化步骤三&#xff1a;数据标准化步骤四&#xff1a;结果处理步骤一&#xff1a;读数据 步骤一&#xff1a;读数据 X xlsread(‘blind date.xlsx’); % 读取Excel文件中的相亲数据 详…

基于高德地图的怀化旅发精品路线智能规划导航之旅

目录 前言 一、2025湖南旅发 1、关于旅发 2、精品路线发布 二、高德技术赋能 1、地理编码服务简介 2、地理编码服务参数介绍 3、自驾路径规划 4、自驾路径规划参数介绍 三、Java集成高德地图服务 1、业务调用时序 2、Java地理编码服务 3、Java路径规划 4、整体集成…

OpenCV实战1.信用卡数字识别

1. 任务说明 有如下几张信用卡&#xff0c;我们需要根据模板匹配出其中的数字&#xff0c;进行卡号的识别2. Debug源码 cursor的debug&#xff1a;launch.json&#xff1a; {// 使用 IntelliSense 了解相关属性。 // 悬停以查看现有属性的描述。// 欲了解更多信息&#xff0c;请…

Spring Security 深度学习(一): 基础入门与默认行为分析

目录1. 引言&#xff1a;为何选择Spring Security&#xff1f;2. 核心概念&#xff1a;认证 (Authentication) 与 授权 (Authorization)2.1 什么是认证 (Authentication)&#xff1f;2.2 什么是授权 (Authorization)&#xff1f;2.3 安全性上下文 (SecurityContext)3. Spring B…

数学建模--模糊综合评价法

一、概念 模糊综合评价法是一种基于模糊数学的综合评价方法。它针对评价过程中存在的模糊性&#xff08;如 “好”“较好”“差” 等模糊概念&#xff09;&#xff0c;通过建立模糊集合&#xff0c;将定性评价转化为定量评价&#xff0c;从而对具有多种属性的评价对象做出全面、…

科普 | 5G支持的WWC架构是个啥(2)?

为解决有线固定宽带与无线移动宽带融合问题&#xff0c;3GPP在5G中推出了WWC系统架构。它将两种接入类型统一融合到5G核心网络。这有助于运营商简化控制、简化管理并为终端用户提供一致服务&#xff1b;其中&#xff1a;一、5G核心组件包括&#xff1a;AMF(接入和移动性管理功能…