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模式的重要组成部分

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

kubernetes有ingress-controler以及没有外部loadbalancer 的情况下使用istio-gateway.

那就配置一个ingress-使用已有ingress-controler代理istio-gateway class创建的gateway svc来公开。

基于ssm的家政服务中介网(java项目+文档+源码)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于ssm的闲一品交易平台。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 家政服务中介网的主要使用者分为…

关于Idea无法正常启动

编辑这个文件 最后一行 加上 pause 双击文件 会显示报错信息

Spring声明式事务以及事务传播行为

Spring声明式事务以及事务传播行为 Spring声明式事务1.编程式事务2.使用AOP改造编程式事务3.Spring声明式事务 事务传播行为 如果对数据库事务不太熟悉&#xff0c;可以阅读上一篇博客简单回顾一下&#xff1a;MySQL事务以及并发访问隔离级别 Spring声明式事务 事务一般添加到…

R语言数据操纵:常用函数

这篇文章主要介绍R语言中处理循环&#xff0c;排序&#xff0c;总结重要信息的常用函数。 处理循环的函数 lapply函数 这个函数就是俗称的一句话循环函数&#xff0c;不同于while循环或者for循环&#xff0c;这个函数可以实现一句话就是一个循环的效果。 具体格式为lapply(…

网络编程核心概念解析:IP地址、端口号与网络字节序深度探讨

⭐小白苦学IT的博客主页 ⭐初学者必看&#xff1a;Linux操作系统入门 ⭐代码仓库&#xff1a;Linux代码仓库 ❤关注我一起讨论和学习Linux系统 本节重点 认识IP地址, 端口号, 网络字节序等网络编程中的基本概念; 1.前言 网络编程&#xff0c;作为现代信息社会中的一项核心技术&…

c# wpf template ItemTemplate 简单试验

1.概要 ItemTemplate&#xff0c;定义列表类的控件形状 2.代码 2.1 控件 <Window x:Class"WpfApp2.Window2"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"http://schemas.microsoft.com/winfx/2006/xaml"xml…

Spyder5.4.3升级到5.5.1

1.升级提示 2.根据升级提示,打开conda终端,分别执行上面命令 conda update anaconda 升级成功 安装Spyder5.5.1 用管理员身份运行下面命令: conda install spyder5.5.1 输入y进行更新 安装完成后出现一个done. 成功升级Spyder为5.5.1

(二)whatsapp 语音通话基本实现

Whatsapp VoiceCall 客户端通过websocket连接到服务器&#xff0c;客户端发起语音通话请求&#xff0c;并且完成必要的协商之后&#xff0c;就可以直接将语音数据发送给服务器&#xff0c;服务器接收到对方的语音数据之后也会通过websocket将语音数据转发给客户端。 websocke…

【数据分析面试】6.计算对话总数(SQL)

题目&#xff1a;计算对话总数 给定了名为 messenger_sends 的消息发送表格&#xff0c;找出总共有多少个唯一的对话。 注&#xff1a;在某些记录中&#xff0c;receiver_id 和 sender_id 从初始消息中互换了。这些记录应视为同一个对话。 示例&#xff1a; 输入&#xff1…

训练营十六天(二叉树part03)

104.二叉树的最大深度 力扣题目链接(opens new window) 题目 给定一个二叉树&#xff0c;找出其最大深度。 二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。 说明: 叶子节点是指没有子节点的节点。 示例&#xff1a; 给定二叉树 [3,9,20,null,null,15,7]&…