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

【声明】本题目来源于卡码网(题目页面 (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.

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

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

相关文章

Google play 应用批量下架的可能原因及应对指南

想必大多数上架马甲包或矩阵式上架的开发者们&#xff0c;都遭遇过应用包批量被下架、账号被封的情况。这很令人苦恼&#xff0c;那造成这种情况的可能原因有哪些呢&#xff1f;以及如何降低这种情况发生&#xff1f; 1、代码问题 通常上架成功后被下架的应用&#xff0c;很可…

PyTorch入门之Tensor综合-含操作/运算、机器学习的关系、稠密张量与稀疏张量的定义等

PyTorch入门之Tensor综合-含操作/运算、机器学习的关系、稠密张量与稀疏张量的定义等 Tensor的理解 数学中有标量、向量和矩阵的概念&#xff0c;它们的维度分别是0、1、2。其中&#xff1a; 标量可以看成的一个数字&#xff0c;1&#xff0c;标量中元素的位置固定。向量可以…

014集:python访问互联网:网络爬虫实例—python基础入门实例

以pycharm环境为例&#xff1a; 首先需要安装各种库(urllib&#xff1a;requests&#xff1a;Openssl-python等) python爬虫中需要用到的库&#xff0c;大致可分为&#xff1a;1、实现 HTTP 请求操作的请求库&#xff1b;2、从网页中提取信息的解析库&#xff1b;3、Python与…

Gin 框架之Cookie与Session

文章目录 一、Cookie和Session的由来二、Cookie简介1. 什么是Cookie2. Cookie规范3. 安全性4. Cookie 关键配置 三、Session简介1. 什么是Session2. Session 安全性3. 如何让客户端携带 sess_id 四、使用 Gin 的 Session 插件4.1 介绍4.2 基本使用 五、 session与store5.1 会话…

【算法】递归

递归 递归初始递归&#xff1a;数列求和递归的应用&#xff1a;任意进制转换递归深度限制递归可视化&#xff1a;分形树递归可视化&#xff1a;谢尔宾斯基Sierpinski三角形递归的应用&#xff1a;汉诺塔递归的应用&#xff1a;探索迷宫 分治策略和递归优化问题兑换最少个数硬币…

蓝桥杯回文日期判断

思想&#xff1a;对于回文数的判断方法&#xff0c;最快的就是取其中一半的字符串长度&#xff0c;为s&#xff0c;然后将其进行翻转为s’ &#xff0c;再把两者进行拼接即可保证是回文数&#xff0c;这样子就解决了枚举所有回文数的问题。 注意点&#xff1a; 要求必须是有效…

机器学习:holdout法(Python)

import pandas as pd import numpy as np from sklearn.preprocessing import LabelEncoder, StandardScaler # 类别标签编码&#xff0c;标准化处理 from sklearn.decomposition import PCA # 主成分分析 import matplotlib.pyplot as plt from sklearn.model_selection impor…

积分梳状滤波器CIC原理与实现

CIC&#xff08;Cascade Intergrator Comb&#xff09;&#xff1a;级联积分梳状滤波器&#xff0c;是由积分器和梳状滤波器级联而得。滤波器系数为1&#xff0c;无需对系数进行存储&#xff0c;只有加法器、积分器和寄存器&#xff0c;资源消耗少&#xff0c;运算速率高&#…

docker部署项目,/var/lib/docker/overlay2目录满了如何清理?

docker部署项目&#xff0c;/var/lib/docker/overlay2目录满了如何清理&#xff1f; 一、问题二、解决1、查看 /var/lib/docker 目录&#xff08;1&#xff09;、containers 目录&#xff08;2&#xff09;、volumes 目录&#xff08;3&#xff09;、overlay2 目录 2、清理&…

【高并发内存池】定长内存池实现

目录 一、项目介绍 二、内存池介绍 2.1、池化技术 2.2、内存池 2.3、内存池主要解决的问题 三、malloc了解 四、设计定长内存池 五、和new(实际malloc)对比性能测试 一、项目介绍 当前项目实现的高并发内存池原型是goole的开源项目tcmalloc&#xff0c;tcmalloc全称为T…

Qt —— 编译Qt5版本QFTP库,并实现连接服务、获取列表、上传、下载、删除文件等操作(附源码、附基于Qt5编译好的QFTP库)

示例效果1 示例效果2 介绍 QFTP是Qt4的库,Qt5改用了QNetworkAccessManager来代替。但是Qt5提供的QNetworkAccessManager仅支持FTP的上传和下载,所以只能将QFTP库编译为Qt5的库来进行调用。 QFTP在Github的下载地址:https://github.com/qt/qtftp 客户端源码生成的release结果…

739.每日温度 496.下一个更大元素 I

739.每日温度 496.下一个更大元素 I 739.每日温度 力扣题目链接(opens new window) 请根据每日 气温 列表&#xff0c;重新生成一个列表。对应位置的输出为&#xff1a;要想观测到更高的气温&#xff0c;至少需要等待的天数。如果气温在这之后都不会升高&#xff0c;请在该位…

MSVS C# Matlab的混合编程系列1 - 看似简单的问题引出

前言&#xff1a; 问题提出&#xff0c;如何把Matlab(本文简称MT)的算法集成到Visual Studio(本文简称VS)里面运行&#xff1f; 本文&#xff0c;通过编制一个MT中最简单的加法函数&#xff0c;我们把他做成 MSVS C#能够使用的动态库&#xff0c;说明了MSVS C# 和 MT集成的最…

Adobe全新AI驱动的Premiere Pro功能消除了枯燥的音频编辑任务

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

亚信安慧AntDB数据库:开辟数据库新纪元

在数字化时代的快速发展中&#xff0c;数据成为企业的核心资产&#xff0c;而数据库则是管理和利用这些数据的关键。 为满足企业日益复杂的混合负载场景和混合数据类型业务需求&#xff0c;亚信科技AntDB提出了全新的“超融合”理念。该理念将多引擎、多能力融合在一起&#x…

Docker(二)安装指南:主要介绍在 Linux 、Windows 10 和 macOS 上的安装

作者主页&#xff1a; 正函数的个人主页 文章收录专栏&#xff1a; Docker 欢迎大家点赞 &#x1f44d; 收藏 ⭐ 加关注哦&#xff01; 安装 Docker Docker 分为 stable test 和 nightly 三个更新频道。 官方网站上有各种环境下的 安装指南&#xff0c;这里主要介绍 Docker 在…

Java进阶-Tomcat发布JavaWeb项目

对于云服务器&#xff0c;程序员一般不会陌生&#xff0c;如果项目需要发布到现网&#xff0c;那么服务器是必不可缺的一项硬性条件&#xff0c;那么如何在云服务器上部署一个项目&#xff0c;需要做哪些配置准备&#xff0c;下面就由本文档为大家讲解&#xff0c;本篇以Tomcat…

windows vscode jsoncpp cmake c++ 构建项目

jsoncpp的编译和使用推荐文章&#xff1a;jsoncpp的编译和使用 | 爱编程的大丙 (subingwen.cn)https://www.subingwen.cn/cpp/jsoncpp/从这个链接下载jsoncpp-master&#xff1a;https://github.com/open-source-parsers/jsoncpp 可以把这个文件夹名字改成jsoncpp&#xff0c;…

街机模拟游戏逆向工程(HACKROM)教程:[10]68K汇编add指令

我们之前已经介绍了move指令&#xff0c;从本章开始&#xff0c;我们会一步步介绍更多的M68K指令。 简介&#xff1a; add :加法指令 该指令的作用是[源操作数]加[目的操作数]&#xff0c;结果传递至[目的操作数]&#xff0c;[源操作数]保持不变。 例子&#xff1a;…

MySQL复合查询 内外连接

目录 前言&#xff1a; 多表查询&#xff1a; 显示部门号为10的部门名&#xff0c;员工名和工资 : 显示各个员工的姓名&#xff0c;工资&#xff0c;及工资级别: 自连接 显示员工FORD的上级领导的编号和姓名(mgr是员工领导的编号&#xff09; 子查询 单行子查询&#…