C++设计模式——Observer观察者模式

news2024/11/14 15:20:44

一,观察者模式的定义

观察者模式是一种行为型设计模式,又被称为"发布-订阅"模式,它定义了对象之间的一对多的依赖关系,当一个对象的状态发生变化时,所有依赖于它的对象都会收到通知并自动更新。

观察者模式的关注点是对象之间的通信以及被观察对象的状态。

观察者模式在现实生活中的抽象实例:

报纸订阅:报纸的内容发生变化时,订阅了该报纸的读者们都会收到通知并阅读最新的内容。

股票投资:股票的价格发生波动时,投资者们会根据最新价格修改相应的投资决策。

天气预报:当天气发生变化时,订阅了该服务的用户们会收到通知。

网络论坛:当论坛中有新的帖子或回复出现时,论坛的用户们会收到通知并可以参与讨论。

二,观察者模式的结构

观察者模式主要包含以下组件:

1.被观察者(Subject):

被观察的对象,它的内部包含了观察者对象的集合,并提供了添加、通知和删除观察者对象的统一接口。

2.观察者(Observer):

接收Subject通知的对象,它订阅了Subject的状态,并提供了更新操作的统一接口。

3.具体的被观察者(ConcreteSubject):

包含Subject类接口的具体实现,维护了观察者的列表,自身状态发生变化时通知所有的观察者。

4.具体的观察者(ConcreteObserver):

包含Observer类接口的具体实现,提供了更新操作的具体实现细节,一旦收到Subject的通知便进行更新操作。

组件之间的工作步骤如下:

1.被观察者维护一个观察者的列表,并提供了管理和通知观察者的方法。

2.观察者与被观察者绑定(attach),并将自己添加到观察者列表中。

3.当被观察者的状态发生变化时,开始通知观察者,通知的方式一般是遍历观察者列表,遍历时会调用每个观察者的更新方法。

4.观察者完成具体的更新操作。

对应UML类图:

三,观察者模式代码样例

Demo1:subject只完成通知

#include <iostream>
#include <vector>

class Observer {
public:
    virtual void update() = 0;
};

class ConcreteObserver : public Observer {
public:
    ConcreteObserver(std::string name)
    {
        observer_name = name;
    }
    void update() {
        std::cout << observer_name <<  " received notify." << std::endl;
    }
private:
    std::string observer_name = "";
};

class Subject {
private:
    //观察者集合
    std::vector<Observer*> observers;
public:
    //添加观察者
    void attach(Observer* observer) {
        observers.push_back(observer);
    }
    //移除观察者
    void detach(Observer* observer) {
        for (auto it = observers.begin(); it != observers.end(); ++it) {
            if (*it == observer) {
                observers.erase(it);
                break;
            }
        }
    }
    //通知观察者
    void notify() {
        for (auto observer : observers) {
            observer->update();
        }
    }
};

int main() {
    Subject subject;
    ConcreteObserver observer1("observer_1");
    ConcreteObserver observer2("observer_2");
    subject.attach(&observer1);
    subject.attach(&observer2);
    subject.notify();
    subject.detach(&observer2);
    subject.notify();
    return 0;
}

运行结果:

observer_1 received notify.
observer_2 received notify.
observer_1 received notify.

Demo2:subject完成通知并传参

#include <iostream>
#include <vector>

class Observer {
public:
    virtual void update(int data) = 0;
};

class ConcreteObserver : public Observer {
public:
    ConcreteObserver(std::string name)
    {
        observer_name = name;
    }
    void update(int data) override {
        std::cout << observer_name << " received data: " << data << std::endl;
    }
private:
    std::string observer_name = "";
};

class Subject {
public:
    virtual void attach(Observer* observer) = 0;
    virtual void detach(Observer* observer) = 0;
    virtual void notify(int data) = 0;
};

class ConcreteSubject : public Subject {
private:
    std::vector<Observer*> observers;
public:
    void attach(Observer* observer) override {
        observers.push_back(observer);
    }
    void detach(Observer* observer) override {
        for (auto it = observers.begin(); it != observers.end(); ++it) {
            if (*it == observer) {
                observers.erase(it);
                break;
            }
        }
    }
    void notify(int data) override {
        for (auto observer : observers) {
            observer->update(data);
        }
    }
};

int main() {
    ConcreteSubject subject;
    ConcreteObserver observer1("observer_1");
    ConcreteObserver observer2("observer_2");
    ConcreteObserver observer3("observer_3");
    subject.attach(&observer1);
    subject.attach(&observer2);
    subject.attach(&observer3);
    subject.notify(30);
    subject.detach(&observer1);
    subject.detach(&observer2);
    subject.notify(40);
    return 0;
}

运行结果:

observer_1 received data: 30
observer_2 received data: 30
observer_3 received data: 30
observer_3 received data: 40

四,观察者模式的应用场景

事件驱动编程:GUI界面开发时,监听用户在界面的各种操作,如按钮点击、窗口关闭等。
监控服务开发:当系统状态发生变化时(例如磁盘空间不足),工具会收到通知。
消息队列开发:基于"消费者-生产者"模式进行通信,当消息队列中有新的消息时,消费者会收到通知。

五,观察者模式的优缺点

观察者模式的优点:
符合"开闭原则"的要求。
支持广播的通信方式。
支持事件驱动编程。
可以动态添加观察者,代码扩展性好。
观察者模式的缺点:
每次状态变化都要遍历所有观察者,性能开销大。
每次状态变化都要通知所有的观察者,通信时间变长。
观察者数量过多会使代码的可读性变差。
当有多个客户端操作观察者的删除时,会带来数据安全问题。

六,代码实战

Demo1:基于观察者模式实现的模拟时钟定时
#include <iostream>
#include <vector>

class Subject;

class Observer
{
public:
    virtual ~Observer() = default;
    virtual void Update(Subject&) = 0;
};

class Subject
{
public:
    virtual ~Subject() = default;
    void Attach(Observer& o) { observers.push_back(&o); }
    void Detach(Observer& o)
    {
        observers.erase(std::remove(observers.begin(), observers.end(), &o));
    }
    void Notify()
    {
        for (auto* o : observers) {
            o->Update(*this);
        }
    }
private:
    std::vector<Observer*> observers;
};

class ClockTimer : public Subject
{
public:
    void SetTime(int hour, int minute, int second)
    {
        this->hour = hour;
        this->minute = minute;
        this->second = second;
        Notify();
    }
    int GetHour() const { return hour; }
    int GetMinute() const { return minute; }
    int GetSecond() const { return second; }
private:
    int hour;
    int minute;
    int second;
};

class DigitalClock : public Observer
{
public:
    explicit DigitalClock(ClockTimer& s) : subject(s) { subject.Attach(*this); }
    ~DigitalClock() { subject.Detach(*this); }
    void Update(Subject& theChangedSubject) override
    {
        if (&theChangedSubject == &subject) {
            Draw();
        }
    }
    void Draw()
    {
        int hour = subject.GetHour();
        int minute = subject.GetMinute();
        int second = subject.GetSecond();
        std::cout << "Digital time is " << hour << ":"
            << minute << ":"
            << second << std::endl;
    }
private:
    ClockTimer& subject;
};

class AnalogClock : public Observer
{
public:
    explicit AnalogClock(ClockTimer& s) : subject(s) { subject.Attach(*this); }
    ~AnalogClock() { subject.Detach(*this); }
    void Update(Subject& theChangedSubject) override
    {
        if (&theChangedSubject == &subject) {
            Draw();
        }
    }
    void Draw()
    {
        int hour = subject.GetHour();
        int minute = subject.GetMinute();
        int second = subject.GetSecond();
        std::cout << "Analog time is " << hour << ":"
            << minute << ":"
            << second << std::endl;
    }
private:
    ClockTimer& subject;
};

int main()
{
    ClockTimer timer;
    DigitalClock digitalClock(timer);
    AnalogClock analogClock(timer);
    timer.SetTime(14, 41, 36);
    timer.SetTime(18, 00, 00);
}

运行结果:

Digital time is 14:41:36
Analog time is 14:41:36
Digital time is 18:0:0
Analog time is 18:0:0

Demo2:基于观察者模式实现的模拟天气预报

#include <iostream>
#include <vector>

class Observer {
public:
    virtual void update(float temperature, float humidity, float pressure) = 0;
};

class WeatherStation {
private:
    float temperature;
    float humidity;
    float pressure;
    std::vector<Observer*> observers;
public:
    void registerObserver(Observer* observer) {
        observers.push_back(observer);
    }
    void removeObserver(Observer* observer) {
    }
    void notifyObservers() {
        for (Observer* observer : observers) {
            observer->update(temperature, humidity, pressure);
        }
    }
    void setMeasurements(float temp, float hum, float press) {
        temperature = temp;
        humidity = hum;
        pressure = press;
        notifyObservers();
    }
};

class Display : public Observer {
public:
    void update(float temperature, float humidity, float pressure) {
        std::cout<< " Display: Temperature = " << temperature
            << " °C, Humidity = " << humidity
            << " %, Pressure = " << pressure << " hPa"
            << std::endl;
    }
};

int main() {
    WeatherStation weatherStation;
    Display display1;
    Display display2;
    weatherStation.registerObserver(&display1);
    weatherStation.registerObserver(&display2);
    weatherStation.setMeasurements(25.5, 60, 1013.2);
    weatherStation.setMeasurements(24.8, 58, 1014.5);
    return 0;
}

运行结果:

Display: Temperature = 25.5 °C, Humidity = 60 %, Pressure = 1013.2 hPa
Display: Temperature = 25.5 °C, Humidity = 60 %, Pressure = 1013.2 hPa
Display: Temperature = 24.8 °C, Humidity = 58 %, Pressure = 1014.5 hPa
Display: Temperature = 24.8 °C, Humidity = 58 %, Pressure = 1014.5 hPa

七,参考阅读

https://sourcemaking.com/design_patterns/observer
https://www.modernescpp.com/index.php/the-observer-pattern/
https://www.geeksforgeeks.org/observer-pattern-c-design-patterns/
https://refactoringguru.cn/design-patterns/observer

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

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

相关文章

13、Django Admin创建两个独立的管理站点

admin文件 from .models import Epic, Event, EventHero, EventVillain from django.contrib.admin import AdminSiteclass EventAdminSite(AdminSite):site_header "Events管理"site_title "欢迎您&#xff01;"index_title "管理员"even…

AI自动生成PPT哪个软件好?如何自动生成专业级PPT?

新学期伊始&#xff0c;准备开学演讲稿的你是否还在为制作PPT而烦恼&#xff1f;别担心&#xff0c;现在有了AI的帮助&#xff0c;生成专业且吸引人的PPT变得轻而易举。 本文将为你揭秘4种高效的AI自动生成PPT的方法&#xff0c;让你在新学期的演讲中脱颖而出。无论是简洁明了…

畅游5G高速网络:联发科集成Wi-Fi6E与蓝牙5.2的系统级单芯片MT7922

这周末,除非外面下钞票,否则谁也拦不住我玩《黑神话悟空》(附:两款可以玩转悟空的显卡推荐) IPBrain平台君 集成电路大数据平台 2024年09月03日 17:28 北京 联发科一直以创新技术追赶市场需求…… “不努力向前游就会被海浪拍回岸边…” 芯片设计公司产品层出不穷,想要站…

Redis集群搭建以及用idea连接集群

一、redis的集群搭建&#xff1a; 判断一个是集群中的节点是否可用,是集群中的所用主节点选举过程,如果半数以上的节点认为当前节点挂掉,那么当前节点就是挂掉了,所以搭建redis集群时建议节点数最好为奇数&#xff0c;搭建集群至少需要三个主节点,三个从节点,至少需要6个节点。…

Datawhle X 李宏毅苹果书AI夏令营深度学习笔记之——卷积神经网络

卷积神经网络简介 卷积神经网络&#xff08;Convolutional Neural Network, CNN&#xff09;是一种深度学习模型&#xff0c;尤其擅长处理图像和视频等高维度的数据。CNN 通过模仿人类视觉系统的工作方式&#xff0c;自动学习数据中的空间层次结构&#xff0c;使得它在计算机视…

我找到了一个让ChatGPT稳定通过草莓测试的方法,百试百灵!

大家好&#xff0c;我是木易&#xff0c;一个持续关注AI领域的互联网技术产品经理&#xff0c;国内Top2本科&#xff0c;美国Top10 CS研究生&#xff0c;MBA。我坚信AI是普通人变强的“外挂”&#xff0c;专注于分享AI全维度知识&#xff0c;包括但不限于AI科普&#xff0c;AI工…

windows C++ 并行编程-并发和UWP(三)

控制执行线程 Windows 运行时使用 COM 线程模型。 在此模型中&#xff0c;根据对象处理其同步的方式&#xff0c;对象被托管在不同的单元中。 线程安全对象托管在多线程单元 (MTA) 中。 必须通过单个线程访问的对象托管在单线程单元 (STA) 中。 在具有 UI 的应用程序中&#…

系统找不到指定的文件怎么解决?

把U盘插在电脑上&#xff0c;当我打开U盘中的文件时&#xff0c;弹窗提示系统找不到指定的文件&#xff0c;这是什么情况&#xff1f;有谁遇到过吗&#xff1f;大家有没有解决办法&#xff1f; 这个问题可能大家并不陌生&#xff0c;可能也曾遇到过&#xff0c;造成问题出现的原…

DriveLM的baseline复现

DriveLM是一篇很有意思的工作&#xff0c;把自动驾驶跟MLLM结合到一起了&#xff0c;实现端到端的感知or决策规划。 Repo&#xff1a;https://github.com/OpenDriveLab/DriveLM 该工作是基于nuScenes数据集做的&#xff0c;官方paper里给出了数据的具体构建方式&#xff0c;感…

云计算之ECS

目录 一、ECS云服务器 1.1 ECS的构成 1.2 ECS的实例规格 1.3 镜像 1.4 磁盘 1.5 安全组 1.6 网络 1.7 产品结构 二、块存储介绍 2.1 快存储定义 2.2 块存储性能指标 2.3 快存储常用操作-云盘扩容 2.4 块存储常见问题 三、快照介绍 3.1 快照定义 3.2 快照常见问题…

《python语言程序设计》第8章第12题生物信息:找出基因,生物学家使用字母A C T和G构成字符2串建模一个基因组(下)

一、上一个版本 抱歉各位兄弟我感觉这道题我现在的能力有限,不纠结了跳过去.等第3刷的时候解决吧. 可能彼岸就在眼前,但是我累了.等下次吧 这个版本中div_text函数已经可以很好的划分字符串了 但是我发现了一个问题.它间隔字符效果如下 genome_text TTATGTTTTAAGGATGGGGCGTTAG…

CSS - 搜索框小动效

点击搜索框动画变长&#xff0c;搜索框有内容不变&#xff0c;无内容失去焦点&#xff0c;变回原来模样。<div :class"type true ? s_r_z : s_r" click"onChange"><div class"input_s"><input blur"handleBlur" v-mo…

QTC++联合编程之解决代码语句块折叠并中文注释代码块

目录&#xff1a; 一&#xff0c;前言二&#xff0c;解决方法2.1直接折叠代码段落&#xff0c;不命名2.2折叠代码段落并注释&#xff08;中/英文&#xff09;命名2.3使用模板 三&#xff0c;参考文章 一&#xff0c;前言 如果从C#或者从其他语言学习过&#xff0c;一定会感叹ID…

Android实习面经整理第一篇

蔚来Android实习面经 一面(2024/3/11 35min) 自我介绍聊我的本专业说一说MVP架构,MVVM架构 MVP:V层持有P层,用户点击View,把数据发给P层,P层持有M层,然后P层把V层的数据发给M层获取其他数据,最后M层获取完数据后把数据还给P层,更新V层。P层也有V层的引用。MVVM:V层…

使用ElementUI + Vue框架实现学生管理系统前端页面设计

目录 一.什么是ElementUI&#xff1f; 二.使用ElementUI和Vue-cli搭建前端页面 三.具体步骤 1.创建vue-cli项目 2.分析 3.创建组件 四.总结 一.什么是ElementUI&#xff1f; ElementUI是一种网站快速成型工具&#xff0c;一套为开发者&#xff0c;设计师准备的基于Vue2.…

江协科技stm32————11-4 SPI通信协议

目录 SPI外设简介 SPI框图 波特率控制 SPE&#xff08;SPI使能&#xff09; 配置主从模式 四种模式的选择 发送和接收数据缓冲区状态 I2C基本结构 1. SPI模式选择 2. 时钟极性和相位&#xff08;CPOL和CPHA&#xff09; 3. 波特率设置 4. 数据帧格式 5. NSS引脚管…

Steam游戏截图方法

Steam游戏截图方法 截图快捷键 Steam游戏自带截图功能&#xff0c;在游戏中无需复杂的快捷键&#xff0c;仅需按下F12快捷键便可立即截图&#xff0c;官方说明如下。下文介绍使用方法。 查看截图 退出游戏后&#xff0c;在Steam界面点击查看 - 截图&#xff0c;即可查看截…

AndroidLogger 适配好了,但没法上架

看到有网友还在用之前的 AndroidLogger 版本&#xff0c;让我感动再次花了 2个月适配新的Notepad&#xff0c;总算搞完了&#xff0c;但是Notepad作者反了&#xff0c;我没法上架啊。 演示视频地址&#xff1a; Notepad安卓日志插件&#xff0c;支持文件管理和截屏&#xff0c…

无需前端技能:如何使用 Amis 框架简化页面开发

Amis 是一个由百度开源的前端低代码框架&#xff0c;它允许开发者通过 JSON 配置文件来快速生成各种后台管理页面。Amis 的设计理念是通过配置而非编码来实现页面的构建&#xff0c;这使得即使是不熟悉前端技术的开发者也能快速上手。Amis 提供了丰富的组件库和模板&#xff0c…

Mqtt消费端实现的几种方式

此处测试的mqtt的Broker是使用的EMQX 5.7.1&#xff0c;可移步至https://blog.csdn.net/tiantang_1986/article/details/140443513查看详细介绍 一、方式1 添加必要的依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spr…