C++ 设计模式之观察者模式

news2025/1/11 7:54:06

【声明】本题目来源于卡码网(题目页面 (kamacoder.com))

【提示:如果不想看文字介绍,可以直接跳转到C++编码部分】


【设计模式大纲】

         前面的文章介绍了创建型模式和结构型模式,今天开始介绍行为型模式。


【简介】什么是观察者模式(第13种模式)

        观察者模式(发布-订阅模式)属于⾏为型模式,定义了⼀种⼀对多的依赖关系,让多个观察者对象同时监听⼀个主题对象,当主题对象的状态发⽣变化时,所有依赖于它的观察者都得到通知并被⾃动更新。
        观察者模式依赖两个模块:

  • Subject (主题):也就是被观察的对象,它可以维护⼀组观察者,当主题本身发⽣改变时就会通知观察者。
  • Observer (观察者):观察主题的对象,当“被观察”的主题发⽣变化时,观察者就会得到通知并执⾏相应的处理。

 

        使⽤观察者模式有很多好处,⽐如说观察者模式将主题和观察者之间的关系解耦,主题只需要关注⾃⼰的状态变化,⽽观察者只需要关注在主题状态变化时需要执⾏的操作,两者互不⼲扰,并且由于观察者和主题是相互独⽴的,可以轻松的增加和删除观察者,这样实现的系统更容易扩展和维护。


【基本结构】

        观察者模式依赖主题和观察者,但是⼀般有4个组成部分:

  •  主题Subject : ⼀般会定义成⼀个接⼝,提供⽅法⽤于注册、删除和通知观察者,通常也包含⼀个状态,当状态发⽣改变时,通知所有的观察者。
  • 观察者Observer : 观察者也需要实现⼀个接⼝,包含⼀个更新⽅法,在接收主题通知时执⾏对应的操作。
  • 具体主题ConcreteSubject : 主题的具体实现, 维护⼀个观察者列表,包含了观察者的注册、删除和通知⽅法。
  • 具体观察者ConcreteObserver : 观察者接⼝的具体实现,每个具体观察者都注册到具体主题中,当主题状态变化并通知到具体观察者,具体观察者进⾏处理。

 【基本实现】

        根据上⾯的类图,我们可以写出观察者模式的基本实现(以Java代码作以说明):

1. 主题接口

// 主题接⼝ (主题)
interface Subject {
    // 注册观察者
    void registerObserver(Observer observer);
    // 移除观察者
    void removeObserver(Observer observer);
    // 通知观察者
    void notifyObservers();
}

2. 观察者接口

// 观察者接⼝ (观察者)
interface Observer {
    // 更新⽅法
    void update(String message);
}

3. 具体主题的实现

// 具体主题实现
class ConcreteSubject implements Subject {
    // 观察者列表
    private List<Observer> observers = new ArrayList<>();
    // 状态
    private String state;
    // 注册观察者
    @Override
    public void registerObserver(Observer observer) {
        observers.add(observer);
    }
    // 移除观察者
    @Override
    public void removeObserver(Observer observer) {
        observers.remove(observer);
    }
    // 通知观察者
    @Override
    public void notifyObservers() {
        for (Observer observer : observers) {
        // 观察者根据传递的信息进⾏处理
        observer.update(state);
        }
    }
    // 更新状态
    public void setState(String state) {
        this.state = state;
        notifyObservers();
    }
}

4. 具体观察者的实现

// 具体观察者实现
class ConcreteObserver implements Observer {
    // 更新⽅法
    @Override
    public void update(String message) {
    }
}

【使用场景】

        观察者模式特别适⽤于⼀个对象的状态变化会影响到其他对象,并且希望这些对象在状态变化时能够⾃动更新的情况。 ⽐如说在图形⽤户界⾯中,按钮、滑动条等组件的状态变化可能需要通知其他组件更新,这使得观察者模式被⼴泛应⽤于GUI框架,⽐如Java的Swing框架。
        此外,观察者模式在前端开发和分布式系统中也有应⽤,⽐较典型的例⼦是前端框架Vue , 当数据发⽣变化时,视图会⾃动更新。⽽在分布式系统中,观察者模式可以⽤于实现节点之间的消息通知机制,节点的状态变化将通知其他相关节点。


【编码部分】

1. 题目描述

        小明所在的学校有一个时钟(主题),每到整点时,它就会通知所有的学生(观察者)当前的时间,请你使用观察者模式实现这个时钟通知系统。

        注意点:时间从 0 开始,并每隔一个小时更新一次。

2. 输入描述

        输入的第一行是一个整数 N(1 ≤ N ≤ 20),表示学生的数量。 接下来的 N 行,每行包含一个字符串,表示学生的姓名。 最后一行是一个整数,表示时钟更新的次数。

3. 输出描述

        对于每一次时钟更新,输出每个学生的姓名和当前的时间。

4. C++编码实例

/**
* @version Copyright (c) 2024 NCDC, Servo。 Unpublished - All rights reserved
* @file ObserverMode.hpp
* @brief 观察者模式
* @autor 写代码的小恐龙er
* @date 2024/01/15
*/

#include <iostream>
#include <string>
#include <vector>

using namespace std;

// 前置声明

// 观察者接口类(再次声明 接口类都是属于基类  需要后续具体实现类来重载操作)
class Observer;
// 主题接口
class Subject;
// 具体主题的实现 -- 时钟
class Clock;
// 具体的观察者 -- 学生
class Student;

// 观察者接口类
class Observer
{
// 接口函数 -- 更新主题的变化
public:
    Observer(){}
    virtual void UpdateTime(int hour) = 0;
};

// 主题接口
class Subject
{
// 主题对观察者的接口函数
public:
    Subject(){}
    // 注册(添加)观察者
    virtual void AddObserver(Observer *observer) = 0;
    // 移除观察者
    virtual void RemoveObserver(Observer *observer) = 0;
    // 通知所有观察者
    virtual void NotifyObservers() = 0;
};


// 具体主题的实现 -- 时钟
class Clock : public Subject
{
// 成员数据
private:
    std::vector<Observer *> _observers;
    int hourTime = 0;

// 成员函数
public:
    // 重载添加函数
    void AddObserver(Observer *observer) override {
        this->_observers.push_back(observer);
    }
    // 重载移除函数
    void RemoveObserver(Observer *observer) override{
        for(unsigned int i = 0; i < _observers.size(); i++){
            if(this->_observers[i] == observer){
                delete this->_observers[i];
                this->_observers[i] = nullptr;
            }
        }
    }
    // 重载通知函数
    void NotifyObservers() override{
        for(Observer *observer : this->_observers){
            observer->UpdateTime(this->hourTime);
        }
    }
    
    // 模拟时间的推移
    void TimeRun(){
        this->hourTime = (this->hourTime + 1);
        NotifyObservers();
    }
    
};

// 具体的观察者 -- 学生
class Student : public Observer
{
// 成员数据
private:
    string _name;

// 成员函数 
public:
    // 重载构造函数 -- 利用学生姓名来具体实例化学生类
    Student(string name){
        this->_name = name;
    }
    
    // 重载更新时间函数
    void UpdateTime(int hour) override {
        std::cout << _name << " " << hour << endl;
    }
};


// 客户端代码
int main()
{
    // 学生数量
    int stuNum = 0;
    // 输入
    std::cin >> stuNum;
    // 创建具体主题 -- 时钟类
    Clock * clocker = new Clock();
    // 创建学生类 -- 用基类创建
    Observer * student = nullptr;
    // 学生姓名
    string name = "";
    // 输入
    int i = 0;
    for(i = 0; i < stuNum; i++)
    {
        std::cin >>  name;
        // 创建学生
        student = new Student(name);
        // 添加学生
        clocker->AddObserver(student);
    }
    
    // 时钟更新次数
    int updateNum = 0;
    // 输入
    std::cin >> updateNum;
    
    for(i = 0; i < updateNum; i++){
        //  调用时间推移函数
        clocker->TimeRun();
    }
    
    // 析构
    if(student != nullptr){
        delete student;
        student = nullptr;
    }
    delete clocker;
    clocker = nullptr;
    
    return 0;
}



......

To be continued.

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

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

相关文章

Oracle AWR报告的生成和解读

Oracle AWR报告的生成和解读 一、AWR报告概念及原理 Oracle10g以后&#xff0c;Oracle提供了一个性能检测的工具&#xff1a;AWR&#xff08;Automatic Workload Repository 自动工作负载库&#xff09;这个工具可以自动采集Oracle运行中的负载信息&#xff0c;并生成与性能相…

评估文字识别准确性的方法与流程

随着信息技术的发展&#xff0c;文字识别技术在各个领域得到了广泛的应用。然而&#xff0c;在实际应用中&#xff0c;如何评估文字识别的准确性&#xff0c;一直是相关领域的一个难题。本文将介绍几种常用的文字识别准确性评估方法&#xff0c;以期为相关领域的研究提供参考。…

Excel 添加复选框或选项按钮(表单控件)

Excel 添加复选框或选项按钮&#xff08;表单控件&#xff09; 要添加复选框或选项按钮&#xff0c;需要使用功能区上的“开发工具”选项卡。 注意: 若要启用“开发工具”选项卡&#xff0c;请按照以下说明进行操作&#xff1a; 在 Excel 2010 和后续版本中&#xff0c;选择“…

主流视频压缩格式

主流的视频压缩格式有很多&#xff0c;它们各自适用于不同的应用场景&#xff0c;如在线流媒体、广播、存档等。 以下是一些广泛使用的视频压缩格式&#xff1a; H.264/AVC (Advanced Video Coding): 目前最为广泛使用的视频压缩标准之一&#xff0c;兼容性极佳&#xff0c;广泛…

【蓝桥杯日记】第二篇——递归问题的处理

目录 前言 递归 递归解决的问题 递归的三要素 递归的练习&#xff08;由浅入深&#xff09; 1.循环改为递归 2.斐波那契 3.汉诺塔问题 总结 前言 大家好呀&#xff01;我是大雄&#xff01;一个菜鸡&#xff01;接下来的几个月和大家分享一下自己在备战蓝桥中遇到的…

微信小程序(四)页面跳转

注释很详细&#xff0c;直接上代码 新增内容 1.相对路径页面跳转 2. 绝对路径页面跳转 index.wxml <!-- navigator是块级元素&#xff0c;占一整行 --> <!-- 页面跳转url&#xff0c;相对路径 --> <navigator url"../logs/logs"><button type&…

【现代密码学】笔记6--伪随机对象的理论构造《introduction to modern cryphtography》

【现代密码学】笔记6--伪随机对象的理论构造《introduction to modern cryphtography》 写在最前面6 伪随机对象的理论构造 写在最前面 主要在 哈工大密码学课程 张宇老师课件 的基础上学习记录笔记。 内容补充&#xff1a;骆婷老师的PPT 《introduction to modern cryphtogr…

Vue中的全局组件与局部组件

聚沙成塔每天进步一点点 本文内容 ⭐ 专栏简介1. 全局组件的原理2. 局部组件的原理3. 组件注册的影响与考虑因素全局组件的使用场景&#xff1a;局部组件的使用场景&#xff1a; 4. 组合使用全局组件与局部组件 ⭐ 写在最后 ⭐ 专栏简介 Vue学习之旅的奇妙世界 欢迎大家来到 Vu…

Android 捕获native异常

11.使用google官方提供的breakpad 地址&#xff1a;Android 捕获native异常 https://github.com/google/breakpad 2.编译breakpad的so文件 2.1设置需要编译的架构 3.编译完&#xff0c;在build\intermediates\cmake\debug\obj目录下获取对应架构的so包 4.编译一个错误的so包…

MIT 6s081 lab3:page tables

Lab3: page tables 作业地址&#xff1a;Lab: page tables (mit.edu) 本实验的目标&#xff1a;修改页表、简化从用户态拷贝数据到内核态的方法 其实页表就几个操作&#xff1a;创建页表、添加PTE项&#xff0c;查找PTE项&#xff0c;清空PTE项&#xff0c;释放PTE对应的物理…

聚观早报 |一加Ace 3鸣沙金开售;地平线征程6将发布

聚观早报每日整理最值得关注的行业重点事件&#xff0c;帮助大家及时了解最新行业动态&#xff0c;每日读报&#xff0c;就读聚观365资讯简报。 整理丨Cutie 1月16日消息 一加Ace 3鸣沙金开售 地平线征程6将发布 互动短剧《谍影成双》首曝PV 台积电发布四季度财报 保时捷…

JVM实战(21)——jstat实战(2)

作者简介&#xff1a;大家好&#xff0c;我是smart哥&#xff0c;前中兴通讯、美团架构师&#xff0c;现某互联网公司CTO 联系qq&#xff1a;184480602&#xff0c;加我进群&#xff0c;大家一起学习&#xff0c;一起进步&#xff0c;一起对抗互联网寒冬 学习必须往深处挖&…

Proxy的使用方法和13种拦截操作

前言 proxy是ES6新推出的方法,功能很强大。属于元编程,也就是修改js本身的一些东西。可以对数组,对象,函数等引用类型的对象进行一些复杂的操作。 其中,大部分人应该最熟悉的莫过于vue3中使用proxy替换了defineProperty,而且还实现了本身defineProperty不能实现的一些东西。 …

Vue-API

$parent 和 $children $parent 父传子--在子组件中使用&#xff0c;放在计算属性、生命周期中&#xff1a; $children 子传父--方法中使用&#xff1a; $nextTick: $ref: 操作dom $set、$delete:

UML-实现图(组件图和部署图)

实现图是从系统的层次来描述的&#xff0c;描述硬件的组成和布局&#xff0c;描述软件系统划分和功能实现。 UML-实现图&#xff08;组件图和部署图&#xff09; 一、组件图1.组件图的元素&#xff08;1&#xff09;组件&#xff08;2&#xff09;接口&#xff08;3&#xff09…

linux系统nginx工具的一些应用和基于nginx做虚拟主机

nginx高级应用 虚拟目录监控模块配置文件创建用户名密码客户端访问 限制传输速度&#xff08;服务层&#xff09; nginx虚拟主机基于ip基于域名基于端口 nginx配置文件中的每个语句要以 ; 结尾 虚拟目录 配置文件中的server块中编辑&#xff1a;location /test {alias /usr/…

SparkSQL初体验

SparkSQL初体验 命令式的 API RDD 版本的 WordCount val conf new SparkConf().setAppName("ip_ana").setMaster("local[6]") val sc new SparkContext(conf)sc.textFile("hdfs://master:9000/dataset/wordcount.txt").flatMap(_.split("…

Visual Studio 2019 ctrl+f 呼出查找和替换窗口

有时候 ctrlshiftf 呼出查找和替换窗口不起作用&#xff0c;可能和其它程序的快捷键冲突&#xff0c;解决方案&#xff1a; ------------英文版本------------ 依次点击VS菜单栏中的 Tools - Options - Environment - Keyboard: 1. 在右侧的 Show commands containing: 文本框输…

单列的堆叠柱状图

目的 MSingleColumnStackBarChart类被设计用于创建只有单列的堆叠柱状图&#xff0c;用于血糖数据的统计。以下是封装这个类的目的的详细描述&#xff1a; 抽象复杂性&#xff1a; 通过创建MSingleColumnStackBarChart类&#xff0c;你将复杂的MPAndroidChart库的使用和配置封…

创意交融:集成自定义报表和仪表盘设计器,实现图标替换

前言 在现代数据分析领域&#xff0c;随着对报表和数据分析的需求不断增长&#xff0c;市场上涌现了许多嵌入式报表工具。这些工具能够与企业现有的OA、ERP、MES、CRM等应用系统深度集成&#xff0c;实现对业务数据的自助式分析。然而&#xff0c;在实际应用中&#xff0c;不同…