设计模式——组件协作模式之观察者模式
文章目录
- 前言
- 一、“组件协作” 模式
- 二、Observer 观察者模式
- 1、动机
- 2、模式定义
- 3、伪代码示例
- ①、第一种方案,最朴素的方式
- ②、第二种方案,重构使得遵循DIP原则:
- ③、进一步的小优化:
- ④、修改使得支持多个观察者:
- 4、结构
- 总结
前言
一、“组件协作” 模式
- 现代软件专业分工之后的第一个结果是 “框架与应用程序的划分”,“组件协作” 模式通过晚期绑定,来实现框架与应用程序之间的松耦合,是二者之间协作时常用的模式。
- 典型模式
- Template Method
- Observer / Event
- Strategy
二、Observer 观察者模式
1、动机
- 在软件构建过程中,我们需要为某些对象建立一种“通知依赖关系”——一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知。如果这样的依赖关系过于紧密,将使软件不能很好地抵御变化。
- 使用面向对象技术,可以将这种依赖关系弱化,并形成一种稳定的依赖关系。从而实现软件体系结构的松耦合。
2、模式定义
定义对象间的一种一对多(变化)的依赖关系,以便当一个对象(Subject)的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。
3、伪代码示例
需求:在一个文件分割器的项目上,增加分割文件时进度条的展示
①、第一种方案,最朴素的方式
FileSplitter1.cpp
//FileSplitter1.cpp
class FileSplitter
{string m_filePath;int m_fileNumber;ProgressBar* m_progressBar; //注:ProgressBar是实现细节,容易变化。是个通知控件public:FileSplitter(const string& filePath, int fileNumber, ProgressBar* progressBar) :m_filePath(filePath), m_fileNumber(fileNumber),m_progressBar(progressBar){}void split(){//1.读取大文件//2.分批次向小文件中写入for (int i = 0; i < m_fileNumber; i++){//...if (m_progressBar != nullptr) {m_progressBar->setValue((i + 1) / m_fileNumber); // 更新进度条}}}
};
MainForm1.cpp
//MainForm1.cpp
class MainForm : public Form
{TextBox* txtFilePath; // 希望分割的文件路径TextBox* txtFileNumber; // 希望分隔的文件个数ProgressBar* progressBar;public:void Button1_Click(){string filePath = txtFilePath->getText();int number = atoi(txtFileNumber->getText().c_str());FileSplitter splitter(filePath, number, progressBar);splitter.split();}
};
存在的问题:违背了 DIP 原则,如果 A 依赖于 B ——编译时“依赖”,即 A 编译的时候 B 要存在。
②、第二种方案,重构使得遵循DIP原则:
FileSplitter2.cpp
//FileSplitter2.cpp
class IProgress{
public:virtual void DoProgress(float value)=0;virtual ~IProgress(){}
};class FileSplitter
{string m_filePath;int m_fileNumber;//ProgressBar* m_progressBar; //注:ProgressBar是实现细节,容易变化。 是个具体通知控件IProgress* m_iprogress; // 抽象通知组件
public:FileSplitter(const string& filePath, int fileNumber, IProgress* iprogress;) :m_filePath(filePath), m_fileNumber(fileNumber),m_iprogress(iprogress){}void split(){ //1.读取大文件//2.分批次向小文件中写入for (int i = 0; i < m_fileNumber; i++){//...float progressValue = m_fileNumber;progressValue = (i + 1) / progressValue;m_iprogress->DoProgress(progressValue); //更新进度条}}
};
MainForm2.cpp
//MainForm2.cpp
class MainForm : public Form, public IProgress
{TextBox* txtFilePath;TextBox* txtFileNumber;ProgressBar* progressBar;public:void Button1_Click(){string filePath = txtFilePath->getText();int number = atoi(txtFileNumber->getText().c_str());FileSplitter splitter(filePath, number, this); splitter.split();}virtual void DoProgress(float value) {progressBar->setValue(value);}
};
方案一是直接去控制进度条,方案二是给你一个接口,我会通过这个接口告诉你现在的进度,但是你怎么显示这个进度就看你接口内部的具体实现
③、进一步的小优化:
FileSplitter2.cpp
//FileSplitter2.cpp
class IProgress{
public:virtual void DoProgress(float value)=0;virtual ~IProgress(){}
};class FileSplitter
{string m_filePath;int m_fileNumber;//ProgressBar* m_progressBar; //注:ProgressBar是实现细节,容易变化。是个具体通知控件IProgress* m_iprogress; // 抽象通知组件
public:FileSplitter(const string& filePath, int fileNumber, IProgress* iprogress;) :m_filePath(filePath), m_fileNumber(fileNumber),m_iprogress(iprogress){}void split(){//1.读取大文件//2.分批次向小文件中写入for (int i = 0; i < m_fileNumber; i++){//...float progressValue = m_fileNumber;progressValue = (i + 1) / progressValue;onProgress(progressValue); }}
protected:virtual void onProgress(float value) { // 以供子类去改写if (m_iprogress != nullptr) {m_iprogress->DoProgress(value);//更新进度条}}
};
目前的实现只能支持一个观察者,此处就是MainForm。
④、修改使得支持多个观察者:
需求:在已支持进度条展示的文件分割器的项目上,增加一个分隔文件时打点的操作
FileSplitter3.cpp
//FileSplitter3.cpp
class IProgress{
public:virtual void DoProgress(float value)=0;virtual ~IProgress(){}
};class FileSplitter
{string m_filePath;int m_fileNumber;List<IProgress*> m_iprogressList; // 抽象通知机制,支持多个观察者public:FileSplitter(const string& filePath, int fileNumber) :m_filePath(filePath), m_fileNumber(fileNumber){}void split(){//1.读取大文件//2.分批次向小文件中写入for (int i = 0; i < m_fileNumber; i++){//...float progressValue = m_fileNumber;progressValue = (i + 1) / progressValue;onProgress(progressValue);//发送通知}}void addIProgress(IProgress* iprogress){m_iprogressList.add(iprogress);}void removeIProgress(IProgress* iprogress){m_iprogressList.remove(iprogress);}protected:virtual void onProgress(float value){List<IProgress*>::iterator itor = m_iprogressList.begin();while (itor != m_iprogressList.end() )(*itor)->DoProgress(value); //更新进度条itor++;}}
};
MainForm3.cpp
//MainForm3.cpp
class MainForm : public Form, public IProgress
{TextBox* txtFilePath;TextBox* txtFileNumber;ProgressBar* progressBar;public:void Button1_Click(){string filePath = txtFilePath->getText();int number = atoi(txtFileNumber->getText().c_str());ConsoleNotifier cn;FileSplitter splitter(filePath, number);// MainForm 是子类,this 指向该类,addIProgress 的参数是个基类指针,基类指针可以指向派生类对象splitter.addIProgress(this); //订阅通知splitter.addIProgress(&cn); //订阅通知splitter.split();splitter.removeIProgress(this);}virtual void DoProgress(float value){progressBar->setValue(value);}
};// 第二个观察者
class ConsoleNotifier : public IProgress {
public:virtual void DoProgress(float value){cout << ".";}
};
4、结构

【注】:
- Observer 对应于 IProgress,Update() 对应于 DoProgress()
- Attach 对应于 addIProgress,Detach 对应于 removeIProgress,Notify 对应于 onProgress, GOF 中建议将这三个方法提出来放到一个父类中,其他的 Subject 继承它,但是此处我们没有将它提出来
- ConcreteSubject 就是 FileSplitter,具体的被观察者;
- ConcreteObserver 对应于 MainForm 和 ConsoleNotifier,具体的观察者。
- 稳定的:Subject、Observer
- 变化的:ConcreteSubject、ConcreteObserver
总结
- 使用面向对象的抽象,Observer 模式使得我们可以独立地改变目标(被观察者)与观察者,从而使二者之间的依赖关系达致松耦合。
- 目标(被观察者)发送通知时,无需指定观察者,通知(可以携带通知信息作为参数)会自动传播。
- 观察者自己决定是否需要订阅通知,目标对象对此一无所知。
- Observer 模式是基于事件的 UI 框架中非常常用的设计模式,也是 MVC 模式的一个重要组成部分。
我的qq:2442391036,欢迎交流!
相关文章:
设计模式——组件协作模式之观察者模式
文章目录 前言一、“组件协作” 模式二、Observer 观察者模式1、动机2、模式定义3、伪代码示例①、第一种方案,最朴素的方式②、第二种方案,重构使得遵循DIP原则:③、进一步的小优化:④、修改使得支持多个观察者: 4、结…...
观察者设计模式知多少
目录 目标 概述 实现 推设计模式 拉设计模式 被动观察者设计模式 目标 熟悉观察者设计模式,了解观察者设计模式的使用场景、具体实现(包括:推设计模式、拉设计模式、被动观察者设计模式)。 概述 一、行为设计模式 行为设…...
Flink之TaskManager内存解析
一、CK失败 Flink任务的checkpoint操作失败大致分为两种情况,ck decline和ck expire: (1)ck decline 发生ck decline情况时,我们可以通过查看JobManager.log或TaskManager.log查明具体原因。其中有一种特殊情况为ck cancel&…...
为何越来越多人不喜欢“试用期六个月”的公司?网友:感觉不靠谱
众所周知,任何一份工作都有试用期,一般是三月左右。但如果你遇到试用期达到半年的公司,你会不会进入? 近日,就有人遇到了此类公司,并对是否要进入该公司犹豫不决。他在论坛上发帖求助:大家是怎…...
单例模式的四种创建方式
前言 单例模式是日常开发中最常见的一种设计模式,常用来做为池对象,或者计数器之类的需要保证全局唯一的场景。 单例模式的目的是保证在整个程序中只存在一个对象实例,使用单例一个前提条件就是构造器私有化,不允许通过new 对象…...
Nginx+Keepalived 中的脑裂现象
如何解决和预防 NginxKeepalived 中会出现的脑裂现象? Nginx是一种高性能的Web服务器和反向代理服务器,可以处理大量并发请求。Keepalived是一种开源软件,用于实现IP负载均衡和故障转移。在Nginx和Keepalived结合使用时,可以通过将多个Ngin…...
04 KVM虚拟化网络概述
文章目录 04 KVM虚拟化网络概述4.1 Linux Bridge4.2 Open vSwitch 04 KVM虚拟化网络概述 为了使虚拟机可以与外部进行网络通信,需要为虚拟机配置网络环境。KVM虚拟化支持Linux Bridge、Open vSwitch网桥等多种类型的网桥。如图1所示,数据传输路径为“虚…...
110页智慧农业解决方案(农业信息化解决方案)(ppt可编辑)
本资料来源公开网络,仅供个人学习,请勿商用,如有侵权请联系删除。 第一部分 智慧农业概述 智慧农业以农业资源为基础、市场为导向、效益为中心、产业化为抓手,面向农业管理部门、农技推广部门、农业企业、农业园区和基地、农业专…...
Java知识体系及聊天室程序
Java知识体系结构梳理如下: 基础语法:Java的基本语法,包括数据类型、运算符、控制语句、数组等。 面向对象编程:Java是一种面向对象的编程语言,需要掌握类、对象、继承、多态等概念。 异常处理:Java提供了…...
java的详细发展历程
Java是一种跨平台、面向对象的编程语言,具有简单性、可移植性、安全性等特点。Java的历史可以追溯到上世纪90年代初期,以下是Java的详细发展历程: 1991年,Sun Microsystems公司的James Gosling和他的团队开始开发一种名为Oak的编程…...
丢石子
I 一堆石子有n个,两人轮流取.先取者第1次可以取任意多个,但不能全部取完.以后每次取的石子数不能超过上次取子数的2倍。取完者胜.先取者负输出"Second win".先取者胜输出"First win". 思路: 任何正整数都可以表示为不连续斐波那契…...
skywalking手动上报一些指标信息
skywalking的相关概念我就不介绍了,有兴趣可以参看官网文档 以下提供以下简单示例手工上报一些对问题排查比较有用的一些信息。当然这些内容你也可以写成探针插件的形式,怎么开发探针插件也自行参考官方文档。此处仅在项目框架层面提供一些简单的示例&am…...
NUMA详解
目录 NUMA简介 NUMA开启与关闭 查看系统是否支持 关闭方法 numactl --hardware介绍 没有安装numactl工具下查看NUMA架构节点数: 查看每个NUMA节点的CPU使用情况: 看每个NUMA节点的内存使用情况: 查看NUMA下指定进程的运行情况 创建…...
H68K在Armbina系统下开AP
背景需求替代路由器,网上找了一大堆都不行 最后成功开启了AP 参考了两篇文章, 一篇是如何创建热点, 一篇是如何开启5G 树莓派4B创建5Ghz WiFi热点 – 风声 https://www.hncldz.com/2020/02/01/%e6%a0%91%e8%8e%93%e6%b4%be4b%e5%88%9b%e5%bb%ba5ghz-wifi%e7%83%ad%e7%82%b…...
还不懂Redis?看完这个故事就明白了!
还不懂Redis?看完这个故事就明白了! 我是Redis 你好,我是Redis,一个叫Antirez的男人把我带到了这个世界上。 说起我的诞生,跟关系数据库MySQL还挺有渊源的。 在我还没来到这个世界上的时候,MySQL过的很辛苦,互联网发展的越来越快,它容纳的数据也越来越多,用户请求也…...
Haproxy负载均衡集群
1.Haproxy支持四层和七层 2.haproxy常用的调度算法? 3.LSV/NGINX/HAPROXT的区别? 4. 5.Haproy负载均衡部署 实验需求 利用Haproxy的运用配置出负载均衡调度器,以此来调用两台Nginx服务器进行工作 实验所需组件 Haproxy服务器:192…...
17.计及电转气协同的含碳捕集与垃圾焚烧虚拟电厂优化调度
说明书 MATLAB代码:计及电转气协同的含碳捕集与垃圾焚烧虚拟电厂优化调度 关键词:碳捕集 虚拟电厂 需求响应 优化调度 电转气协同调度 参考文档:《计及电转气协同的含碳捕集与垃圾焚烧虚拟电厂优化调度》完全复现 仿真平台:…...
企业数字化管理中,数据治理到底怎么“治”
随着信息化、数字化的理念、技术及其应用在社会的方方面面进行扩散,数据的规模和丰富程度已经达到了一个新的高度,所以当下如何更进一步利用好数据,充分发挥数据的价值,将其真正变为高质量的数据资产成为了企业要面对的重要问题&a…...
《HelloGitHub》第 85 期
兴趣是最好的老师,HelloGitHub 让你对编程感兴趣! 简介 HelloGitHub 分享 GitHub 上有趣、入门级的开源项目。 https://github.com/521xueweihan/HelloGitHub 这里有实战项目、入门教程、黑科技、开源书籍、大厂开源项目等,涵盖多种编程语言 …...
自动驾驶人机交互HMI产品技术方案
1. 概述 1.1 目的 本文档描述集卡自动驾驶系统中HMI产品的技术方案,设计人员遵循本方案进行设计,为项目开发实施提供技术方案保障。 1.2 范围 本文档适用于HMI产品项目。本文档用于指导HMI产品项目的UI、前端开发过程。 1.3 术语与缩写 术语/缩写 描述 HMI...
如何快速部署AI视觉瞄准系统:3个版本满足不同需求的终极指南
如何快速部署AI视觉瞄准系统:3个版本满足不同需求的终极指南 【免费下载链接】AI-Aimbot Worlds Best AI Aimbot - CS2, Valorant, Fortnite, APEX, every game 项目地址: https://gitcode.com/gh_mirrors/ai/AI-Aimbot 欢迎来到AI视觉瞄准系统的完整实战教程…...
【独家逆向分析】:Perplexity招聘页埋点数据如何被提取?附Python自动化脚本(限24小时领取)
更多请点击: https://kaifayun.com 第一章:Perplexity薪资数据查询 Perplexity 作为一家以 AI 原生搜索和研究工具著称的科技公司,其薪酬结构长期未公开披露,但可通过多源交叉验证方式获取合理估算。目前主流可信渠道包括 Levels…...
DDR3 颗粒信号定义解析
本文围绕 DDR3 标准信号定义、核心信号工作原理、PCB Layout 等长设计规则及行业常见误区展开,全程聚焦工程实践与底层原理。一、DDR3 标准信号完整清单(x16 位宽基准,x32 位宽对应翻倍)1. 数据信号(Data)表…...
研究助理/项目经理/内容编辑:Hermes Agent 3 类人格模板的 SOUL.md 配置要点
1. 三类人格不是“角色扮演”,而是上下文锚点的工程化切片 大多数人第一次看到 Hermes Agent 的 SOUL.md 配置时,会下意识把它当成一个“AI人设说明书”:研究助理要严谨、项目经理要干练、内容编辑要文雅。这种理解在小规模单次交互中勉强能用,但一旦进入真实研发流程——…...
Kubernetes核心知识点
1.ca-certificates,gnupg,lsb-release三个包的解释. ca-certificates是系统内置的权威根整数数据包,可以让系统正常识别并信任各类网站,避免出现证书异常与访问失败问题。 gnupg是开源的加密与签名校验工具,可用于导入第三方软件源公钥&#…...
GEO建站系统选型避坑指南:如何识别真正有效的服务商
AI搜索渗透率的持续攀升,正在改变企业官网的战略地位。过去,官网是展示门面;现在,官网内容是否能被DeepSeek、豆包、通义千问等大模型理解和引用,直接影响企业在潜在客户第一次提问时能否出现在答案里。这种变化催生了…...
Linux包管理核心:yum机制详解与实战配置指南
1. 项目概述:为什么你需要掌握yum?在Linux的世界里,尤其是以Red Hat、CentOS、Fedora为代表的发行版中,yum(Yellowdog Updater, Modified)是每一位系统管理员和开发者绕不开的核心工具。你可以把它想象成一…...
【Perplexity医疗搜索实战指南】:3大临床决策加速器与5个被90%医生忽略的精准检索技巧
更多请点击: https://codechina.net 第一章:Perplexity医疗搜索的核心价值与临床适配性 Perplexity医疗搜索并非通用搜索引擎的简单垂直化迁移,而是专为临床决策闭环设计的认知增强工具。其核心价值在于将海量异构医学文献、指南更新、药品说…...
Cadence Allegro实战:除了Shape Keepout,还有哪些方法能精准控制铺铜区域?
Cadence Allegro实战:5种精准控制铺铜区域的进阶技巧 在复杂PCB设计中,铺铜区域的控制往往决定了信号完整性和EMC性能。Shape Keepout虽然是设计师最熟悉的工具,但Allegro其实提供了更丰富的"Areas"类命令集。本文将深入解析Route …...
【亲测免费】 探索高效编程新境界:RT809F编程器软件深度体验
探索高效编程新境界:RT809F编程器软件深度体验 【下载地址】RT809F编程器软件 本仓库提供了RT809F编程器的配套软件下载。RT809F是一款高度集成、功能强大的编程和调试工具,专为各种微控制器、闪存、EEPROM以及各种类型的IC设计。通过这款软件࿰…...
