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

news2024/11/18 1:30:08

文章目录

    • 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)
————————————————

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

.net 在ubuntu下动态写入 中文字乱码 解决:ubuntu下添加中文字库

.net 在ubuntu下动态写入图片水印 中文字乱码 解决&#xff1a;ubuntu下添加中文字库 1.安装字体命令 sudo apt install -y fontconfig2.查看已安装的字体 &#xff08;1&#xff09;查看linux已安装字体 fc-list&#xff08;2&#xff09;查看linux已安装中文字体 fc-li…

内存函数(memcpy/memmove/memcmp/memset)

目录 memcpymemcpy函数的模拟实现 memmovememmove函数的模拟实现 memcmpmemset memcpy void * memcpy ( void * destination, const void * source, size_t num ); 函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置这个函数在遇到 ‘\0’ 的时候…

实战Kafka的部署

目录 一、环境准备 二、安装配置jdk8 &#xff08;1&#xff09;Kafka、Zookeeper&#xff08;简称&#xff1a;ZK&#xff09;运行依赖jdk8 三、安装配置ZK &#xff08;1&#xff09;安装 &#xff08;2&#xff09;配置 四、配置Kafka &#xff08;1&#xff09;配置…

day08_面向对象基础_内存关系

零、今日内容 一、作业 二、面向对象 一、作业 package com.qf.homework;import java.util.Arrays;/*** --- 天道酬勤 ---** author QiuShiju* date 2024/2/28* desc*/ public class Homework {public static void main(String[] args) {test();}//写一个方法 用于合并两个int…

golang 泛型详解

目录 概念 ~int vs .int 常见的用途和错误 结论&#xff1a; 概念 Go 在1.18 中添加了泛型&#xff0c;这样Go 就可以在定义时不定义类型&#xff0c;而是在使用时进行类型的定义&#xff0c;并且还可以在编译期间对参数类型进行校验。Go 目前只支持泛型方法&#xff0c;还…

无源蜂鸣器驱动实验

1.原理 计划是&#xff0c;生成不同的7个频率控制蜂鸣器发声&#xff0c;每个音调发声0.5秒。 50MHZ计数0.5秒&#xff0c;50MHZ一个周期20ns&#xff0c;要计数0.5秒 &#xff0c;0.5/0.000_000_02s25000_000个时钟周期。因为是从0开始计数&#xff0c;所以计数的最大值是0-24…

docker版本 jenkins配置gitlab自动部署

前端项目 Build steps pwd npm config set registry https://registry.npm.taobao.org npm -v node -v #npm install npm run build:prod tar -czvf QASystem.tar.gz distpwd cd /data/zhouxy37/vue_deploy tar -zxvf QASystem.tar.gz sudo mv dist QASystem cp -r QASyste…

K8S常用kubectl命令汇总(持续更新中)

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

NGINX的重写与反向代理机制解析

目录 引言 一、重写功能 &#xff08;一&#xff09;if指令 1.判断访问使用的协议 2.判断文件 &#xff08;二&#xff09;return指令 1.设置返回状态码 2.返回指定内容 3.指定URL &#xff08;三&#xff09;set指令 1.手动输入变量值 2.调用其它变量值为自定义变…

web学习笔记(二十二)DOM开始

目录 1.DOM简介 2.DOM树 3.DOM节点 4.查找DOM节点方法汇总 5.查找子结点的属性 5.1父子关系 5.2兄弟关系 6.几个特殊元素的查找 1.DOM简介 DOM&#xff08;Document Object Model&#xff09; 也叫页面文档对象模型&#xff0c;是W3C组织推荐的处理可扩展标记语言(HTML…

蓝桥杯第十二届电子类单片机组程序设计

目录 前言 蓝桥杯大赛历届真题_蓝桥杯 - 蓝桥云课&#xff08;点击查看&#xff09; 单片机资源数据包_2023&#xff08;点击下载&#xff09; 一、第十二届比赛原题 1.比赛题目 2.题目解读 蓝桥杯第十四届电子类单片机组程序设计_蓝桥杯单片机哪一届最难-CSDN博客 二、…

TV-SAM 新型零样本医学图像分割算法:GPT-4语言处理 + GLIP视觉理解 + SAM分割技术

TV-SAM 新型零样本医学图像分割算法&#xff1a;GPT-4语言处理 GLIP视觉理解 SAM分割技术 提出背景TV-SAM 方法论 提出背景 论文&#xff1a;https://arxiv.org/ftp/arxiv/papers/2402/2402.15759.pdf 代码&#xff1a;https://github.com/JZK00/TV-SAM 利用了GPT-4的强大语…

深入理解Spring Security

第1章&#xff1a;Spring Security简介 大家好&#xff0c;我是小黑&#xff0c;在谈到网站安全的时候&#xff0c;Spring Security是个避不开的话题。它就像是个守门员&#xff0c;决定谁能进入我们的网站&#xff0c;又能在网站的哪些角落里走动。简单来说&#xff0c;Sprin…

语音合成(TTS) GPT-SoVITS认知

写在前面 小伙伴推荐&#xff0c;简单了解相对之前试过的其他的TTS项目&#xff0c;GPT-SoVITS的优点简单易用&#xff0c;文档完整&#xff0c;默认的模型效果就很好理解不足小伙伴帮忙指正 不必太纠结于当下&#xff0c;也不必太忧虑未来&#xff0c;当你经历过一些事情的时候…

智能驾驶控制规划理论学习02-基于搜索的路径规划算法

目录 一、路径搜索问题 二、图论基础 三、图搜索方法 1、广度优先搜索&#xff08;BFS&#xff09; bfs与dfs的区别 bfs的搜索过程 bfs的算法实现 2、迪杰斯特拉算法&#xff08;Dijkstra&#xff09; 核心思想 优先级队列 Dijkstra搜索过程 Dijkstra优缺点…

MySQL(基础篇)——函数、约束

一.函数 1.定义 函数是指一段可以直接被另一段程序调用的程序或代码。 2.字符串函数 常见如下&#xff1a; -- 字符串拼接 SELECT CONCAT(hello,MySql) AS CONCAT -- 将字符串全部转为小写 SELECT LOWER(HEllo MYSql) AS LOWER -- 将字符串全部转为大写 SELECT UPPER(Hello…

前端mock数据 —— 使用Apifox mock页面所需数据

前端mock数据 —— 使用Apifox 一、使用教程二、本地请求Apifox所mock的接口 一、使用教程 在首页进行新建项目&#xff1a; 新建项目名称&#xff1a; 新建接口&#xff1a; 创建json&#xff1a; 请求方法&#xff1a; GET。URL&#xff1a; api/basis。响应类型&#xff1…

用Flutter开发App:助力您的移动业务腾飞

一、Flutter简介 Flutter是Google推出的用于构建多平台应用程序的开源UI框架。它使用Dart语言编写&#xff0c;可以编译为原生机器代码&#xff0c;从而提供卓越的性能和流畅的用户体验。 二、Flutter的优势 一套代码&#xff0c;多平台部署&#xff1a;Flutter可以使用一套代…

183896-00-6,Biotin-C3-PEG3-C3-NH2,可以选择性降解靶蛋白

您好&#xff0c;欢迎来到新研之家 文章关键词&#xff1a;183896-00-6&#xff0c;Biotin-C3-PEG3-C3-NH2&#xff0c;Biotin-C3-PEG3-C3-amine&#xff0c;生物素-C3-PEG3-C3-胺 一、基本信息 【产品简介】&#xff1a;Biotin-PEG3-C3-NH2是一种PROTAC linker&#xff0c;…

Redis在中国火爆,为何MongoDB更受欢迎国外?

一、概念 Redis Redis&#xff08;Remote Dictionary Server&#xff09;是一个使用ANSI C编写的开源、支持网络、基于内存、分布式、可选持久性的键值对存储数据库。Redis是由Salvatore Sanfilippo于2009年启动开发的&#xff0c;首个版本于同年5月发布。 MongoDB MongoDB…