C++观察者模式代码实例

文章目录

    • C++观察者模式代码实例一
    • C++观察者模式代码实例二

C++观察者模式代码实例一

下面是一个简单的C++观察者模式的实现示例,这里仅给出核心代码框架,完整的工程应包含对应的头文件声明及必要的#include指令等。

// 观察者接口(Observer)
class IObserver {
public:
    virtual ~IObserver() {}
    virtual void update(const std::string& message) = 0; // 更新方法
};

// 主题接口(Subject)
class ISubject {
public:
    virtual ~ISubject() {}
    virtual void registerObserver(IObserver* observer) = 0; // 注册观察者
    virtual void removeObserver(IObserver* observer) = 0; // 移除观察者
    virtual void notifyObservers(const std::string& message) = 0; // 通知观察者
};

// 具体主题(ConcreteSubject)
class ConcreteSubject : public ISubject {
private:
    std::vector<IObserver*> observers; // 存储观察者列表

public:
    void registerObserver(IObserver* observer) override {
        observers.push_back(observer);
    }

    void removeObserver(IObserver* observer) override {
        observers.erase(std::remove(observers.begin(), observers.end(), observer), observers.end());
    }

    void notifyObservers(const std::string& message) override {
        for (const auto& obs : observers) {
            obs->update(message);
        }
    }

    // 其他业务逻辑,当有状态改变时调用notifyObservers()
    void changeState(const std::string& newState) {
        // 假设这里有某种状态变更逻辑
        std::string message = "状态已更新至:" + newState;
        notifyObservers(message);
    }
};

// 具体观察者(ConcreteObserver)
class ConcreteObserver : public IObserver {
public:
    void update(const std::string& message) override {
        std::cout << "Observer received message: " << message << std::endl;
        // 在此处响应状态变更,执行观察者的相关操作
    }
};

// 使用示例
int main() {
    ConcreteSubject subject;
    ConcreteObserver observer1;
    ConcreteObserver observer2;

    subject.registerObserver(&observer1);
    subject.registerObserver(&observer2);

    subject.changeState("New State"); // 当状态变化时,所有观察者都会被通知

    return 0;
}

在这个例子中,ConcreteSubject 是具体的主题类,它可以添加和移除观察者,并在状态变化时通过调用 notifyObservers() 方法通知所有的观察者。ConcreteObserver 类则是实现了 IObserver 接口的观察者,当收到 update() 调用时,会执行相应的更新操作。主程序中创建了一个主题实例和两个观察者实例,并将观察者注册到主题上,当主题状态发生变化时,观察者接收到通知并作出反应。

在实际应用中,观察者模式通常用于设计事件驱动的系统,或者是当对象的状态变化需要自动通知其他对象时。以下是对上述代码实例的进一步阐述:

  1. 应用场景举例:假设你正在构建一个气象站系统,其中ConcreteSubject代表气象站,它记录并报告实时天气数据。ConcreteObserver可以代表各种订阅气象信息的实体,如天气预报网站、农业自动化系统、交通管理系统等。

  2. 主体(Subject):当气象站检测到天气数据发生改变时,例如温度、湿度或风速变化,它会调用notifyObservers()方法,通知所有已注册的观察者。

  3. 观察者(Observer):每一个观察者在接收到update()调用时,会按照自己的需求处理这个更新消息。比如,天气预报网站会在页面上实时更新数据,农业自动化系统会据此调整灌溉计划,交通管理系统则可能据此发布道路预警信息。

  4. 灵活性:观察者模式提供了很好的灵活性,因为新增加的观察者只需实现IObserver接口,并通过主题的registerObserver()方法注册自己,就可以开始接收更新通知,而不需要修改原有的主体或其它观察者。

  5. 松耦合:通过观察者模式,主题与观察者之间的耦合度较低,两者之间通过接口进行交互,这有助于简化系统的维护和扩展。

总之,通过这个简单的C++观察者模式实例,我们可以了解到如何构建一个具有动态通知机制的系统,使得当对象状态改变时,它的所有依赖对象都能得到及时的更新。在实际开发中,观察者模式广泛应用于GUI编程、事件处理、游戏开发、分布式系统等多种场景。

C++观察者模式代码实例二

另外,观察者模式还可以结合C++的一些现代特性进行优化,例如使用智能指针来管理观察者生命周期,以避免内存泄漏问题。下面是一个使用std::shared_ptr改进后的观察者列表管理的例子:

#include <iostream>
#include <vector>
#include <memory>

// 观察者接口(Observer)
class IObserver {
public:
    virtual ~IObserver() {}
    virtual void update(const std::string& message) = 0;
};

// 主题接口(Subject)
class ISubject {
public:
    virtual ~ISubject() {}
    virtual void attach(std::shared_ptr<IObserver> observer) = 0;
    virtual void detach(std::shared_ptr<IObserver> observer) = 0;
    virtual void notifyObservers(const std::string& message) = 0;
};

// 具体主题(ConcreteSubject)
class ConcreteSubject : public ISubject {
private:
    std::vector<std::shared_ptr<IObserver>> observers;

public:
    void attach(std::shared_ptr<IObserver> observer) override {
        observers.push_back(observer);
    }

    void detach(std::shared_ptr<IObserver> observer) override {
        observers.erase(
            std::remove(observers.begin(), observers.end(), observer),
            observers.end()
        );
    }

    void notifyObservers(const std::string& message) override {
        for (const auto& obs : observers) {
            obs->update(message);
        }
    }

    // 其他业务逻辑...
};

// 具体观察者(ConcreteObserver)
class ConcreteObserver : public IObserver {
public:
    void update(const std::string& message) override {
        std::cout << "Observer received message: " << message << std::endl;
        // 在此处响应状态变更,执行观察者的相关操作
    }
};

// 使用示例
int main() {
    ConcreteSubject subject;
    auto observer1 = std::make_shared<ConcreteObserver>();
    auto observer2 = std::make_shared<ConcreteObserver>();

    subject.attach(observer1);
    subject.attach(observer2);

    subject.notifyObservers("New State"); // 当状态变化时,所有观察者都会被通知

    return 0;
}

在这个改进版中,观察者列表现在存储的是指向观察者的智能指针,这样当观察者不再需要时,可以自动释放其占用的内存,有效防止了内存泄漏。同时,这也使得主题类与观察者之间的关系管理更加安全和可靠。

此外,观察者模式还可以与C++11/14/17的新特性结合起来,以更好地适应现代C++编程实践。例如:

  1. Lambda 表达式:在某些情况下,你可以使用lambda表达式作为临时观察者,这样就不必为一次性任务专门创建一个类。例如:
subject.attach([&](const std::string& message) {
    std::cout << "Anonymous observer received message: " << message << std::endl;
});

subject.notifyObservers("New State");
  1. std::functionstd::bind:如果你需要传递已经存在的函数或成员函数作为观察者,可以使用std::functionstd::bind来包装这些函数。例如,假设你有一个打印日志的全局函数或类成员函数:
void logMessage(const std::string& message) {
    std::cout << "Log system received message: " << message << std::endl;
}

// 全局函数
subject.attach(logMessage);

// 成员函数,假设有一个Logger类
class Logger {
public:
    void log(const std::string& message) {
        std::cout << "Logger received message: " << message << std::endl;
    }
};

Logger logger;
subject.attach(std::bind(&Logger::log, &logger, std::placeholders::_1));

通过这些现代C++特性的应用,观察者模式变得更加灵活,可以适应更多样化的场景和需求。同时,结合智能指针和其他内存管理工具,观察者模式的实现可以变得更安全、更易于维护。

除此之外,观察者模式还可以与C++17中的std::variantstd::visit配合使用,以处理不同类型的通知消息。例如,当主题拥有多种状态需要向观察者发送时,可以定义一个包含所有可能状态类型的std::variant变量:

#include <variant>

enum class WeatherDataType { Temperature, Humidity, WindSpeed };

struct WeatherData {
    WeatherDataType type;
    double value;
};

class ConcreteSubject : public ISubject {
    // ...
    void notifyObservers(WeatherData data) override {
        for (const auto& obs : observers) {
            obs->update(data);
        }
    }
};

class AdvancedObserver : public IObserver {
public:
    void update(const WeatherData& data) override {
        std::visit(overloaded{
            [&](const auto& value) {
                if (data.type == WeatherDataType::Temperature)
                    handleTemperature(value);
                // 处理其他类型...
            },
        }, data.value);
    }

private:
    void handleTemperature(double temp) {
        std::cout << "Received temperature update: " << temp << std::endl;
    }

    // 处理其他数据类型的方法...
};

在此案例中,WeatherData结构体包含了数据类型和对应的值,观察者可以根据type字段判断所接收数据的具体类型,并通过std::visit调用相应的方法进行处理。

综上所述,观察者模式与现代C++特性相结合,可以构建出更加灵活、类型安全且易于维护的系统,适用于各种复杂的事件驱动和状态变更通知场景。

python推荐学习汇总连接:
50个开发必备的Python经典脚本(1-10)

50个开发必备的Python经典脚本(11-20)

50个开发必备的Python经典脚本(21-30)

50个开发必备的Python经典脚本(31-40)

50个开发必备的Python经典脚本(41-50)
————————————————

​最后我们放松一下眼睛
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/414246.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

nginx介绍及编译安装

nginx介绍 是一个流行的开源的高性能的HTTP和反向代理服务器&#xff0c;也可以用作邮件代理服务器。它以其高性能、稳定性、丰富的功能集和低资源消耗而闻名 nginx特点 高性能&#xff1a; Nginx以其高效的事件驱动架构而闻名&#xff0c;能够处理大量并发连接而不会消耗过多…

面试官:集群部署时的分布式 Session 如何实现?

欢迎来到百战百胜&#xff01;我们致力于为广大IT从业者、学生和爱好者提供全面、实用的资源和服务。加入我们的聊天群&#xff0c;这里有专业大佬为你提供有价值的建议和指导&#xff01; 微信搜索&#xff1a;IT开DD那点小事 更多访问&#xff1a;www.besthub.tech ​ 面试题…

C++入门05 类与对象

图源&#xff1a;文心一言 听课笔记简单整理&#xff0c;供小伙伴们参考~&#x1f95d;&#x1f95d; 第1版&#xff1a;听课的记录代码~&#x1f9e9;&#x1f9e9; 编辑&#xff1a;梅头脑&#x1f338; 审核&#xff1a;文心一言 目录 &#x1f433;类与对象 &#x1…

尚硅谷(SpringCloudAlibaba微服务分布式)学习代码Eureka部分

1.项目结构 2.cloud2024 pom <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.a…

【C++】auto、范围for循环、宏函数和内联函数

auto、范围for、内联函数、宏函数和nullptr 一、auto — 类型推导的魔法&#xff08;C 11)1、auto 是什么&#xff1f;2、工作原理3、优势4、限制和注意事项 二、范围for (C11)1、基本语法2、优势3、工作原理4、注意事项5、C11&#xff1a; 范围 for 循环的扩展&#xff1a; 三…

MCU最小系统电路设计(以STM32F103C8T6为例)

目录 一、何为最小系统&#xff1f; 二、最小系统电路设计 1.电源 &#xff08;1&#xff09;各种名词解释 &#xff08;2&#xff09;为什么会有VDD_1 _2 _3区分&#xff1f; &#xff08;3&#xff09;Mirco USB &#xff08;4&#xff09;5v->3.3v滤波电路 &#…

尚硅谷webpack5笔记2

Loader 原理 loader 概念 帮助 webpack 将不同类型的文件转换为 webpack 可识别的模块。 loader 执行顺序 分类pre: 前置 loadernormal: 普通 loaderinline: 内联 loaderpost: 后置 loader执行顺序4 类 loader 的执行优级为:pre > normal > inline > post 。相…

算法day01_ 27. 移除元素、977.有序数组的平方

推荐阅读 从零开始学数组&#xff1a;深入浅出&#xff0c;带你掌握核心要点 初探二分法 再探二分法 系统的纪录一下刷算法的过程&#xff0c;之前一直断断续续的刷题&#xff0c;半途而废&#xff0c;现在重新开始。话不多说&#xff0c;开冲&#xff01; 27.移除元素 题目 给…

雾锁王国Enshrouded服务器CPU内存配置怎么选择?

雾锁王国/Enshrouded服务器CPU内存配置如何选择&#xff1f;阿里云服务器网aliyunfuwuqi.com建议选择8核32G配置&#xff0c;支持4人玩家畅玩&#xff0c;自带10M公网带宽&#xff0c;1个月90元&#xff0c;3个月271元&#xff0c;幻兽帕鲁服务器申请页面 https://t.aliyun.com…

php基础学习之错误处理(其一)

一&#xff0c;错误处理的概念 错误处理指的是系统(或者用户)在执行某些代码的时候&#xff0c;发现有错误&#xff0c;就会通过错误处理的形式告知程序员&#xff0c;俗称报错 二&#xff0c;错误分类 语法错误&#xff1a;书写的代码不符合 PHP 的语法规范&#xff0c;语法错…

协议-http协议-基础概念01-发展历程-http组成-http是什么-相关的应用-相关的协议

发展历程-http组成-http是什么-相关的应用-相关的协议 参考来源&#xff1a; 极客时间-透视HTTP协议(作者&#xff1a;罗剑锋)&#xff1b; 01-HTTP的发展历程 1989 年&#xff0c;任职于欧洲核子研究中心&#xff08;CERN&#xff09;的蒂姆伯纳斯 - 李&#xff08;Tim Ber…

异地文件共享慢如何解决?

如今&#xff0c;随着信息化的迅猛发展&#xff0c;异地文件共享已经成为了许多企业和个人不可或缺的一部分。在实际应用过程中&#xff0c;我们常常会遇到异地文件共享速度缓慢的问题。本文将深入探讨异地文件共享慢的原因&#xff0c;并介绍一种解决方案——天联组网&#xf…

R语言数学建模(一)—— 基础知识

R语言数学建模&#xff08;一&#xff09;—— 基础知识 文章目录 R语言数学建模&#xff08;一&#xff09;—— 基础知识前言一、建模软件1.1 软件建模的基础1.2 模型的分类1.3 不同类型模型间的联系1.4 一些术语1.5 建模如何适应数据分析过程 二、Tidyverse基础2.1 tidyvers…

【mysql】1812 - Tablespace is missing for table `job`.`xxl_job_log`.

打开表提示&#xff1a; 1812 - Tablespace is missing for table job.xxl_job_log. 1812-表“job”缺少表空间xxl_job_log。 尝试删除表重建表 DROP TABLE IF EXISTS job.xxl_job_log; 提示&#xff1a; 1051 - Unknown table job.xxl_job_log 1051-未知表“job.xxl_job_lo…

什么是去中心化云计算?

去中心化云计算是一种新型的云计算方式&#xff0c;它与传统的中心化云计算不同&#xff0c;将数据和计算任务分布到多个节点上&#xff0c;而不是将数据集中存储在中心服务器上。这种云计算方式具有许多优势&#xff0c;包括提高数据安全性、降低运营成本、增强可扩展性和灵活…

【监督学习之模型选择与评估】

曾梦想执剑走天涯&#xff0c;我是程序猿【AK】 目录 简述概要知识图谱1. 模型选择&#xff1a;2. 模型评估&#xff1a;3. 超参数调优&#xff1a;4. 最终模型选择&#xff1a;实践建议&#xff1a; 详细内容总结 简述概要 了解模型选择与评估 知识图谱 在监督学习中&…

CV论文--2024.2.28

source:CV论文--2024.2.28 1、StreamDiffusion: A Pipeline-level Solution for Real-time Interactive Generation 中文标题&#xff1a;StreamDiffusion: 一个用于实时交互生成的管道级解决方案 简介&#xff1a;我们介绍了StreamDiffusion&#xff0c;这是专为实时交互式图…

RK3568平台 RTC时间框架

一.RTC时间框架概述 RTC&#xff08;Real Time Clock&#xff09;是一种用于计时的模块&#xff0c;可以是再soc内部&#xff0c;也可以是外部模块。对于soc内部的RTC&#xff0c;只需要读取寄存器即可&#xff0c;对于外部模块的RTC&#xff0c;一般需要使用到I2C接口进行读取…

prometheus+grafana监控nginx的简单实现

1.编译安装NGINX 加入编译安装nginx-module-vts模块,目的是为了获取更多的监控数据(虚拟主机&#xff0c;upstream等) nginx下载 http://nginx.org/download/nginx-1.20.2.tar.gz nginx-module-vts下载 https://github.com/vozlt/nginx-module-vts/archive/refs/tags/v0.2…

自然语言处理: 第十三章Xinference部署

项目地址: Xorbitsai/inference 理论基础 正如同Xorbits Inference&#xff08;Xinference&#xff09;官网介绍是一个性能强大且功能全面的分布式推理框架。可用于大语言模型&#xff08;LLM&#xff09;&#xff0c;语音识别模型&#xff0c;多模态模型等各种模型的推理。通…