C++设计模式:观察者模式(三)

1、定义与动机

观察者模式定义:定义对象间的一种1对多(变化)的依赖关系,以便当一个对象(Subject)的状态发生比改变时,所有依赖于它的对象都得到通知并且自动更新

  • 再软件构建过程中,我们需要为某些对象建立一种“通知依赖关系“——一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知。如果这样的依赖关系过于紧密,僵尸软件不能很好地抵御变化。
  • 使用面向对象技术,可以将这种依赖关系弱化,并形成一种稳定的依赖关系。从而实现软件体系结构的松耦合。
2、举例分析
  • 例如点击button将一个文件分割成指定数量的功能,在分割过程中一般有一个合理的类似于进度条的通知功能,可以看到当前分割的进度
  • 很容易写出下面的代码:
    • MainForm在收到触发button按钮的操作之后拿到文件名、需求文件个数就调用FileSplitter进行分割
    • FileSplitter类中依赖一个ProgressBar控件,然后在split分割的过程中计算当前分割的进度,将值会写到ProgressBar控件中用于同步展示
  • 但是这样写并不好:
    • 首先可以思考到的一个问题:ProgressBar写死了,我们只能使用这个控件来实现,如果想换个控件、换个方式或者多个控件来展示似乎不太可行,违背了开闭原则
    • 其次很重要的一点:FileSplitter是一个高层模块, ProgressBar是一个低层模块,形成了高层依赖低层模块。而依赖倒置原则讲的是高层和低层模块都应该依赖其抽象,抽象不能依赖实现细节,实现细节应该依赖抽象。ProgressBar实际是一个实现细节(设置value就能展示)
    • 而这样的一个依赖会产生问题:如果换个形式进度条展示,需要更改需求时很难做到,难以抵御变化。
class FileSplitter{
private:
    string filePath;
    int fileNumber;
    ProgressBar* progressBar;
public:
    FileSplitter(string filepath, int number): filePath(filepath), fileNumber(number){

    }

    void split(){
        // 1. 分批次读取文件大小
        // 2. 分批次向小文件写入
        for(int i = 0;i < fileNumber;i++){
            // 处理...
            // 展示处理的进度条
            progressBar->setValue(1.0*(i+1) / fileNumber);
        }
    }
};

class MainForm: public Form{
private:
    TextBox* txtFilepath;
    TextBox* txtFileNumber;
public:
    void button_event_split(){
        string filePath = txtFilepath->getText();
        int number = atoi(txtFilepath->getText().c_str());
        FileSplitter fileSplitter(filePath, number);
        fileSplitter.split();
    }
};
3、观察者模式
  • 通过分析可知,进度条的展示功能不应该出现在文件分割功能里,应该通知依赖它的地方,进行进度条展示
  • 它只需要向外发送通知即可,至于外面进行如何实现这个进度条展示或者其他功能并不需要它关心
  • 观察者模式的核心就是向外进行通知,外面收到通知的对象如何实现就由它们自己决定
  • 因此观察者模式来做这一需求对上面代码进行改造可以变成如下形式!
3.1、基础优化(一)
class IProgress{
public:
    virtual void doProgress(double value) = 0;
    virtual ~IProgress(){

    }
};

class FileSplitter{
private:
    string filePath;
    int fileNumber;
    IProgress *iProgress;
public:
    FileSplitter(string _filepath, int _number, IProgress *_iProgress): filePath(_filepath), fileNumber(_number), iProgress(_iProgress){

    }

    void split(){
        // 1. 分批次读取文件大小
        // 2. 分批次向小文件写入
        for(int i = 0;i < fileNumber;i++){
            // 处理...
            // 展示处理的进度条
            iProgress->doProgress(1.0*(i+1) / fileNumber);
        }
    }
};

class MainForm: public Form, public IProgress{
private:
    TextBox* txtFilepath;
    TextBox* txtFileNumber;
    ProgressBar *progressBar;
public:
    void button_event_split(){
        string filePath = txtFilepath->getText();
        int number = atoi(txtFilepath->getText().c_str());
        FileSplitter fileSplitter(filePath, number, this);
        fileSplitter.split();
    }
    virtual void doProgress(double value){
        progressBar->setValue(value);
    }
};
3.2、多个观察者(二)
  • 当有多个观察者时,可以通过如下的代码来实现
#include <vector>
class IProgress{
public:
    virtual void doProgress(double value) = 0;
    virtual ~IProgress(){

    }
};

class FileSplitter{
private:
    string filePath;
    int fileNumber;
    vector<IProgress*> progressList;
public:
    FileSplitter(string _filepath, int _number, IProgress *_iProgress): filePath(_filepath), fileNumber(_number), iProgress(_iProgress){

    }

    void split(){
        // 1. 分批次读取文件大小
        // 2. 分批次向小文件写入
        for(int i = 0;i < fileNumber;i++){
            // 处理...
            // 展示处理的进度条
            notify_observers(1.0*(i+1) / fileNumber);
        }
    }

    void add_IProgress(IProgress* iProgress){
        progressList.push_back(iProgress);
    }

    void remove_IProgress(IProgress* iProgress){
        progressList.erase(iProgress);
    }
protected:
    void notify_observers(double value){
        for(auto it = progressList.begin();it != progressList.end();it++){
            (*it)->doProgress(value)
        }
    }
};

class MainForm: public Form, public IProgress{
private:
    TextBox* txtFilepath;
    TextBox* txtFileNumber;
    ProgressBar *progressBar;
public:
    void button_event_split(){
        string filePath = txtFilepath->getText();
        int number = atoi(txtFilepath->getText().c_str());
        FileSplitter fileSplitter(filePath, number, this);
        // 添加额外的观察者
        fileSplitter.add_IProgress(...);
        fileSplitter.split();
    }

    virtual void doProgress(double value){
        progressBar->setValue(value);
    }
};

class ConsoleNotifier: public IProgress{
    virtual void doProgress(double value){
        //...具体实现...
    }
};
4、总结
  • 上面的举例代码中Subject对应FileSplitter、而下面的ConcreteSubject就是那个Vector容器以及加和删除的方法。
  • Observer对应上面的进度条类型
  • ConcreteObserver对应MainForm

在这里插入图片描述

  • 使用面向对象的抽象,Observer模式使得我们可以独立地改变目标与观察者,从而使二者之间的依赖关系达到松耦合
  • 目标发送通知时,无需指定观察者,通知(可以携带通知信息做为参数)会自动传播
  • 观察者自己决定是否需要订阅通知,目标对象对此一无所知
  • Observer模式是基于事件的UI框架中非常常用的设计模式,也是MVC模式的重要组成部分

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

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

相关文章

(文章复现)基于改进秃鹰算法的微电网群经济优化调度研究

参考文献&#xff1a; [1]周辉,张玉,肖烈禧,等.基于改进秃鹰算法的微电网群经济优化调度研究[J].太阳能学报,2024,45(02):328-335. 1.基本原理 微电网群由3个独立的微电网(microgrid , MG)组成,各微电网内部包含光伏(photovoltaic , PV)、风力发电机(windturbine,WT)、电动汽…

h5 笔记3 多媒体素材运用

关于电影编码 我们经常用扩展名来判断文件的类型&#xff0c;但是对于影音文件未必适用&#xff0c;影音文件的文件格式(container)和编码(codec)之间并非绝对相关。决定影音文件播放的关键在于浏览器是否含有适合的影音编解码技术。 笔记来源&#xff1a; ©《HTML5CSS3J…

机器学习笔记 - 深度学习遇到超大图像怎么办?使用 xT 对极大图像进行建模论文简读

作为计算机视觉研究人员,在处理大图像时,避免不了受到硬件的限制,毕竟大图像已经不再罕见,手机的相机和绕地球运行的卫星上的相机可以拍摄如此超大的照片,遇到超大图像的时候,我们当前最好的模型和硬件都会达到极限。 所以通常我们在处理大图像时会做出两个次优选择之一:…

Centos 7 安装通过yum安装google浏览器

在CentOS 7上使用yum安装Google Chrome浏览器稍微复杂一些&#xff0c;因为Chrome并不直接包含在默认的Yum仓库中。按照以下步骤来操作&#xff1a; 1、添加Google Chrome仓库 首先&#xff0c;您需要手动添加Google Chrome的Yum仓库。打开终端&#xff0c;并使用文本编辑器&a…

物联网实战--驱动篇之(四)LoRa应用(modbus)

目录 一、前言 二、板级收发 三、主机请求 四、从机接收及回复 五、主机接收 一、前言 之前两篇分别介绍了modbus和sx1278的驱动&#xff0c;但是都并未具体讲解如何应用&#xff0c;那么这一篇就把两者结合起来&#xff0c;做个小demo&#xff0c;便于理解这两个驱动的使…

2005-2023年各省国内生产总值指数分季度数据

2005-2023年各省国内生产总值指数分季度数据 1、时间&#xff1a;2005-2023年 2、来源&#xff1a;国家统计局、各省统计局 3、指标&#xff1a;地区生产总值指数(上年同期100)_累计值(%) 4、范围&#xff1a;31省 5、时间跨度&#xff1a;季度 6、缺失情况&#xff1a;无…

20240325-1-HMM

HMM 直观理解 马尔可夫链&#xff08;英语&#xff1a;Markov chain&#xff09;&#xff0c;又称离散时间马尔可夫链&#xff08;discrete-time Markov chain&#xff0c;缩写为DTMC&#xff09;&#xff0c;因俄国数学家安德烈马尔可夫&#xff08;俄语&#xff1a;Андре…

Ubuntu 20.04.06 PCL C++学习记录(十六)

[TOC]PCL中点云分割模块的学习 学习背景 参考书籍&#xff1a;《点云库PCL从入门到精通》以及官方代码PCL官方代码链接,&#xff0c;PCL版本为1.10.0&#xff0c;CMake版本为3.16 学习内容 用一组点云数据做简单的平面的分割 源代码及所用函数 源代码 #include<iostr…

Linux 内核:线程的实现

在linux中的线程是轻量级线程&#xff08;Light-Weight-process&#xff0c;LWP&#xff09; 文章目录 线程概念线程实现 线程概念 线程分类 用户级线程内核级线程&#xff0c;没有用户空间&#xff0c;完全工作在内核中&#xff08;下图中没有[]的就是用户级线程&#xff09…

跨服务器迁移nextcloud数据

背景 阿里云最近做活动,99一年的2U2G的服务器,比我原来的1U1G的服务器不知道高到哪里去了,于是决定迁移服务器数据到另一台主机上。原先的计划是直接做一个自定义镜像,然后复制到另一台主机就行,结果发现旧主机是aliyunOS,新主机不想踩这个坑了,决定换成乌班图,因此决定重新搭…

大模型基础知识 - 语言模型及其演进

开场白 人工智能发展到现在&#xff0c;在2个重要领域取得了重大突破&#xff0c;有望达到人类水平&#xff1a; 计算机视觉 &#xff08;Computer Vision, CV&#xff09;-> 希望机器帮助人类处理图像数据自然语言处理&#xff08;Natural Language Processing, NLP&…

基于R语言lavaan结构方程模型(SEM)实践技术应用

原文链接&#xff1a;基于R语言lavaan结构方程模型&#xff08;SEM&#xff09;实践技术应用https://mp.weixin.qq.com/s?__bizMzUzNTczMDMxMg&mid2247596681&idx4&sn08753dd4d3e7bc492d750c0f06bba1b2&chksmfa823b6ecdf5b278ca0b94213391b5a222d1776743609cd…

文章自动生成器

1.前言 最近开始想写一些东西&#xff0c;脑子里也有不少想法&#xff0c;但是自己写又比较费时间&#xff0c;而且还要排版&#xff0c;精修&#xff0c;刚开始写的字数甚少&#xff0c;想法也是断断续续&#xff0c;不连贯&#xff0c;本想放弃&#xff0c;但是想到放弃就太…

HarmonyOS实战开发-如何实现跨应用数据共享实例。

介绍 本示例实现了一个跨应用数据共享实例&#xff0c;分为联系人&#xff08;数据提供方&#xff09;和联系人助手&#xff08;数据使用方&#xff09;两部分&#xff1a;联系人支持联系人数据的增、删、改、查等功能&#xff1b;联系人助手支持同步联系人数据&#xff0c;当…

15.队列集

1.简介 在使用队列进行任务之间的“沟通交流”时&#xff0c;一个队列只允许任务间传递的消息为同一种数据类型&#xff0c;如果需要在任务间传递不同数据类型的消息时&#xff0c;那么就可以使用队列集。FreeRTOS提供的队列集功能可以对多个队列进行“监听”&#xff0c;只要…

【数据结构与算法】—— 二叉树

目录 一、树 1、初识树 2、树的一些概念 3、树的表示形式 二、二叉树 1、初识二叉树 2、两种特殊的二叉树 3、二叉树的性质 4、二叉树的遍历 5、实现一棵二叉树 6、二叉树题目&#xff08;没代码的后面会给补上&#xff09; 一、树 1、初识树 &#xff08;1&…

从人机界面设计黄金三法则视角看 ChatGPT 的界面设计的“好”与“坏”

热门文章推荐&#xff1a; &#xff08;1&#xff09;《为什么很多人工作 3 年 却只有 1 年经验&#xff1f;》&#xff08;2&#xff09;《一文掌握大模型提示词技巧&#xff1a;从战略到战术巧》&#xff08;3&#xff09;《AI 时代&#xff0c;程序员的出路在何方&#xff1…

破晓数据新纪元:隐语隐私计算,携手共创安全智能的未来生态

1.业务背景&#xff1a;安全核对产生的土壤 隐语隐私计算在安全核对业务背景下的应用&#xff0c;主要聚焦于解决企业在数据交换和分析过程中面临的隐私保护问题。 在许多行业中&#xff0c;特别是在金融、医疗、政务等领域&#xff0c;数据的安全核对至关重要&#xff0c;例如…

三流大学毕业,物流专业转行自述:“从月薪4K到现在月入2W+,我做到了哪些?”

我是25岁转行学python的。说实在&#xff0c;转行就是奔着挣钱去的。希望我的经历可以给想转行的朋友带来一点启发和借鉴。 先简单介绍下个人背景&#xff0c;三流大学毕业&#xff0c;物流专业&#xff0c;学习能力一般&#xff0c;没啥特别技能&#xff0c;反正就很普通的一…

《springcloud alibaba》 四 seata安装以及使用

目录 准备调整db配置准备创建数据库 seata配置nacos配置confi.txt下载向nacos推送配置的脚本 启动seata新建项目order-seata项目 订单项目数据库脚本pom.xmlapplication.yml启动类实体类dao类service类controller类feign类mapper类 stock-seata 库存项目数据库脚本pom.xmlappli…