C++实现观察者模式(包含源码)

文章目录

    • 观察者模式
      • 一、基本概念
      • 二、实现方式
      • 三、角色
      • 四、过程
      • 五、结构图
      • 六、构建思路
      • 七、完整代码

观察者模式

一、基本概念

观察者模式(又被称为模型(Model)-视图(View)模式)是软件设计模式的一种。在此种模式中,一个目标物件管理所有相依于它的观察者物件,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。

二、实现方式

观察者模式有很多实现方式,从根本上说,该模式必须包含两个角色:观察者和被观察者。

比如在下面的代码表述中,发布作品的DY博主是被观察者,博主的粉丝是观察者 ,当DY博主(被观察者) 发生某种变化时 (状态改变),就会立即通知博主的粉丝 (观察者 )。

三、角色

1、抽象主题(Subject):

它是一个抽象类,也是所有目标对象的父类。它用一个列表记录当前目标对象有哪些观察者对象,并提供增加(Add)、删除观察者对象(Delete)和通知观察者对象的接口。

2、具体主题(ConcreteSubject):

具体目标类,可以有多个不同的具体目标类,它们同时继承Subject类。一个目标对象就是某个具体目标类的对象,一个具体目标类负责定义它自身的事务逻辑,并在状态改变时通知它的所有观察者对象。

3、抽象观察者(Observer):

观察者类,它也是一个抽象类,是所有观察者对象的父类;它为所有的观察者对象都定义了一个名为show的方法(也叫成员函数)。当目标对象的状态改变时,它就是通过调用它的所有观察者对象的show方法来通知它们的。

4、具体观察者(ConcreteObserver):

具体观察者类,可以有多个不同的具体观察者类,它们同时继承Observer类。一个观察者对象就是某个具体观察者类的对象。每个具体观察者类都要重定义Observer类中定义的show方法,在该方法中实现它自己的任务逻辑,当它被通知的时候(目标对象调用它的show方法)就执行自己特有的任务。

四、过程

实现观察者模式有很多形式,比较直观的一种是使用一种“注册—通知—撤销注册”的形式。

观察者

(Observer)将自己注册到被观察对象(Subject)中,被观察对象将观察者存放在一个容器(Container)里。

被观察对象

被观察对象发生了某种变化,从容器中得到所有注册过的观察者,将变化通知观察者。

撤销观察

观察者告诉被观察者要撤销观察,被观察者从容器中将观察者去除。

五、结构图

img

六、构建思路

我们在前期已经了解了observer和subject的代表意义及用法,下面我们开始构思如何写代码

先写出subject和observer类定义用法

class Observer
{
public:virtual void update(){};virtual std::string getName(){return "";}
};class Subject 
{
public:void Add(Observer *obs);void Delete(Observer *obs);void show();~Subject();
private:std::vector<Observer *>observer;
};

Observer中的成员函数都是虚函数,不需要强调其作用,只需要了解虚函数的用法;

Subject类的成员函数及变量:

void Add(Observer *obs)---------------增加观察者

void Delete(Observer *obs)------------删除观察者

void show()-----------------------------------当被观察者状态发生改变时,及时通知观察者

~Subject()-------------------------------------析构函数,创建了许多观察者,最后需要析构删除

std::vector<Observer *>observer-----存放观察者的信息,观察者存放到vector容器中

具体主题(ConcreteSubject)和具体观察者(ConcreteObserver)的类定义

class ConcreteSubject : public Subject
{
public:std::string getSubjectStatus();void   setSubjectStatus(ConcreteSubject * subject, const std::string &str);
private:std::string subjectStatus;
};class ConcreteObserver : public Observer
{
public:ConcreteObserver(ConcreteSubject *subject,std::string name);std::string getName();void update();
private:std::string name;ConcreteSubject *subject;std::string observerStatus;
};

具体主题(ConcreteSubject)类的成员函数及成员变量

std::string getSubjectStatus() -------------------获得被观察者的状态

void setSubjectStatus(ConcreteSubject * subject, const std::string &str) ------------设置被观察者的状态

std::string subjectStatus---------------------------被观察者状态的变量

具体主题(ConcreteObserver)类的成员函数及成员变量

ConcreteObserver(ConcreteSubject *subject,std::string name)---------构造函数,初始化具体观察者

std::string getName()-----------得到观察者的名字

void update() ----------------------当被观察者状态改变时,提醒观察者

std::string observerStatus-----观察者状态

七、完整代码

observer.h

#include<iostream>
#include<vector>class Observer
{
public:virtual void update(){};virtual std::string getName(){return "";}
};
class Subject 
{
public:void Add(Observer *obs);void Delete(Observer *obs);void show();~Subject();
private:std::vector<Observer *>observer;
};
class ConcreteSubject : public Subject
{
public:std::string getSubjectStatus();void   setSubjectStatus(ConcreteSubject * subject, const std::string &str);
private:std::string subjectStatus;
};
class ConcreteObserver : public Observer
{
public:ConcreteObserver(ConcreteSubject *subject,std::string name);std::string getName();void update();
private:std::string name;ConcreteSubject *subject;std::string observerStatus;
};

observer.cpp

#include "observer.h"
#include<iostream>void Subject::Add(Observer *obs)
{observer.push_back(obs);
}
void Subject::Delete(Observer *obs)
{for(auto i = 0;i < observer.size();i++){if(obs == observer[i]){observer.erase(observer.begin() + i);}}
}
void Subject::show()
{auto it = observer.begin();  //vector<Observer*>::iteratorwhile(it != observer.end())  {(*it)->update();        // it是指向observer元素的地址类似于observer.begin()。*it才相当于 observer[i]it++;}
}
Subject::~Subject()
{std::cout<<"执行析构函数------"<<std::endl;auto it = observer.begin();while(it != observer.end()){std::cout<<"已经删除:"<<(*it)->getName()<<std::endl;delete *it;it++;}observer.clear();
}
std::string ConcreteSubject::getSubjectStatus()
{return subjectStatus;
}
void ConcreteSubject::setSubjectStatus(ConcreteSubject * subject,const std::string &str)
{subjectStatus = str;subject->show();
}
ConcreteObserver::ConcreteObserver(ConcreteSubject *subject,std::string name)
{this->subject = subject;this->name = name;
}
std::string ConcreteObserver::getName()
{return name;
}
void ConcreteObserver::update()
{observerStatus = subject->getSubjectStatus();std::cout<<"您关注的DY博主更新动态了    粉丝: "<<name<<"用户状态: "<<observerStatus<<std::endl;
}

main.cpp

#include "observer.h"
#include <iostream>
int main()
{ConcreteSubject *subject = new ConcreteSubject;subject->Add(new ConcreteObserver(subject,"TMG"));subject->Add(new ConcreteObserver(subject,"WYA"));subject->setSubjectStatus(subject,"在线");std::cout<<std::endl;delete subject;
}

更多资料点击 GitHub 欢迎各位读者去Star

⭐学术交流群Q 754410389 持续更新中~~~

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

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

相关文章

开启编程之门

自我介绍 目前已经大二了&#xff0c;计算机专业在读&#xff0c;是一个热爱编程&#xff0c;做事踏实专注的人。转眼间一年已经过去了&#xff0c;也接触编程一年了&#xff0c;但开始并没有对所学所想进行很好的总结和输出&#xff0c;这一年也有了新的很多感悟与心得&#x…

浅谈双十一背后的支付宝LDC架构和其CAP分析

本人汤波&#xff0c;superthem.com 圆领超级个体创始人&#xff0c;Github page地址&#xff1a;https://tbwork.github.io/ 看到很多人在盗用我的文章&#xff0c;还标记成原创&#xff0c;进行收费&#xff0c;非常令人作呕。 我的所有技术文章全部免费阅读&#xff0c;大家…

PCB走线规则

1、线间距。 这里应该遵循3W规则&#xff0c;所谓3W就是为了减少线间串扰&#xff0c;应保证线间距足够大&#xff0c;当线中心不少于3倍线宽&#xff0c;则可 保持70%的电场不互相干扰。如要达到98%的电场不互相干扰&#xff0c;可使用10W的间距。——这是查阅华为PCB布线规则…

npm 清缓存(重新安装node-modules)

安装node依赖包的会出现失败的情况&#xff0c;如下图所示&#xff1a; 此时 提示有些依赖树有冲突&#xff0c;根据提示 “ this command with --force or --legacy-peer-deps” 执行命令即可。 具体步骤如下&#xff1a; 1、先删除本地node-modules包 2、删掉page-loacl…

el-upload 上传附件(拆解步骤)

目录 1. 看elementui /element-plus 官网案例 2. html部分&#xff1a; 把官网上的搬下来&#xff0c;最好加一个按钮&#xff0c;上传到服务器&#xff08;后端&#xff09; 3. js 部分&#xff1a; 3.1 首先&#xff0c;先定义一个变量&#xff0c;files 3.2 当上传图片…

机器学习(11)---降维PCA

目录 一、概述1.1 维度1.2 sklearn中的降维算法 二、降维实现原理2.1 PCA与SVD2.2 降维实现2.3 降维过程 三、鸢尾花数据集降维3.1 高维数据的可视化3.2 探索降维后的数据3.3 累积可解释方差贡献率曲线 四、选n_components参数方法4.1 最大似然估计自选超参数4.2 按信息量占比选…

WavJourney:进入音频故事情节生成世界的旅程

推荐&#xff1a;使用 NSDT场景编辑器快速搭建3D应用场景 若要正确查看音频生成的强大功能&#xff0c;请考虑以下方案。我们只需要提供一个简单的指令&#xff0c;描述场景和场景设置&#xff0c;模型就会生成一个扣人心弦的音频脚本&#xff0c;突出与原始指令的最高上下文相…

数组和指针笔试题解析之【数组】

目录 前言&#xff1a; 1.一维数组&#xff1a; 2.字符数组 &#xff1a; 2.1题型一&#xff1a; 2.2题型二&#xff1a; 2.3题型三&#xff1a; 3.二维数组 &#xff1a; 前言&#xff1a; 1.数组名的意义&#xff1a; sizeof(数组名)&#xff1a;这里的数组名表示整…

【C++STL基础入门】list的运算符重载和关于list的算法

文章目录 前言一、list运算符1.1 逻辑运算符1.2 赋值运算符 二、list相关算法2.1 查找函数总结 前言 C标准模板库&#xff08;STL&#xff09;是一组强大而灵活的工具&#xff0c;用于处理数据结构和算法。其中&#xff0c;std::list是STL中的一个重要容器&#xff0c;它实现了…

小型网络实验组网

路漫漫其修远兮&#xff0c;吾将上下而求索 时隔多日&#xff0c;没有更新&#xff0c;今日一写&#xff0c;倍感教育的乐趣。如果让我每天发无意义的文章&#xff0c;我宁可不发。 实验拓扑 实验要求 &#xff08;1&#xff09;内网主机采用DHCP分配IP地址 &#xff08;2&…

记录本地Nginx发布vue项目

一、前端&#xff1a;vue-cli-service build 二、下载Nginx&#xff0c;并创建目录&#xff0c;放置静态文件 三、在conf目录下nginx.conf文件配置代理服务 server {listen 8787;server_name localhost;location / {root app/dist; #前端dist包地址index index.html…

Tomcat架构设计及组件详解

继Tomcat配置详解&#xff08;Tomcat配置server.xml详解&#xff09;Tomcat配置详解&#xff08;Tomcat配置server.xml详解&#xff09;_tomcat xml配置https://blog.csdn.net/imwucx/article/details/132166738文章之后&#xff0c;深入的学习tomcat相关知识&#xff0c;对Tom…

基于SSM+Vue的“魅力”繁峙宣传网站

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用Vue技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

数据分享|R语言逻辑回归、Naive Bayes贝叶斯、决策树、随机森林算法预测心脏病...

全文链接&#xff1a;http://tecdat.cn/?p23061 这个数据集&#xff08;查看文末了解数据免费获取方式&#xff09;可以追溯到1988年&#xff0c;由四个数据库组成。克利夫兰、匈牙利、瑞士和长滩。"目标 "字段是指病人是否有心脏病。它的数值为整数&#xff0c;0无…

无代码和低代码平台:程序员的竞争优势

无代码和低代码平台&#xff1a;程序员的竞争优势 无代码和低代码平台&#xff1a;程序员的竞争优势摘要引言1. 了解无代码和低代码平台1.1 无代码和低代码平台的定义无代码平台低代码平台 1.2 它们如何简化应用程序开发1.3 主要的无代码和低代码工具和供应商无代码平台低代码…

【八大经典排序算法】堆排序

【八大经典排序算法】堆排序 一、概述二、思路解读三、代码实现&#xff08;大堆为例&#xff09; 一、概述 堆排序是J.W.J. Williams于1964年提出的。他提出了一种利用堆的数据结构进行排序的算法&#xff0c;并将其称为堆排序。堆排序是基于选择排序的一种改进&#xff0c;通…

蓝牙核心规范(V5.4)10.1-BLE 入门笔记(1)

ble 规范 深入了解蓝牙LE需要熟悉相关的规格。蓝牙LE的架构、程序和协议由一项关键规范完全定义,称为蓝牙核心规范。产品如何使用蓝牙以实现互操作性由两种特殊类型称为配置文件和服务的规范集合所涵盖。图1展示了BLE规范类型及其相互关系。 1.1 蓝牙核心规范 蓝牙核心规范是…

SpringBoot整合Easy-ES实现对ES操作

请确保已有可用的ES&#xff0c;若没有&#xff0c;请移步&#xff1a;Docker安装部署ElasticSearch&#xff08;ES&#xff09; 新建SpringBoot项目 这里是用的springboot版本是2.6.0 引入依赖 <!-- 排除springboot中内置的es依赖,以防和easy-es中的依赖冲突--><…

Redis五大基本数据类型

1、字符串类型 字符串类型相当于 java 中的 String 类型。Redis 中的 String 类型以二进制方式存储&#xff0c;不会做任何的编码转换&#xff0c;因此不仅仅可以存储文本数据、整数、普通的字符串、JSON、xml文件&#xff0c;还可以存储图片、视频、音频。String 存储的种类虽…

插槽指的是什么?插槽的基础用法体验

什么是插槽 插槽(Slot)是 vue 为组件的封装者提供的能力。允许开发者在封装组件时&#xff0c;把不确定的、希望由用户指定的部分定义为插槽。 <template><p>这是MyCom1组件的第1个p标签</p><&#xff01;--通过slot标签&#xff0c;为用户预留内容占位符…