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

2023-09-18 20:26:54

观察者模式

一、基本概念

观察者模式(又被称为模型(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*>::iterator
    while(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 持续更新中~~~

更多推荐

WBS中的控制账户解释

定义控制账户(ControlAccount,简称(CA)是项目管理中用来控制成本和进度的一种方法。控制账户通常是WBS(工作分解结构)的中间层级,用来对下一级的任务进行控制和管理。特点和作用特点控制账户是对WBS中工作包或工作包集合的进一步分解。控制账户是对某一范围内工作、成本和进度的控制点。控制账户可以是项目的基本组

如何解决 503 Service Temporarily Unavailable?

🌷🍁博主猫头虎(🐅🐾)带您GotoNewWorld✨🍁🐅🐾猫头虎建议程序员必备技术栈一览表📖:🛠️全栈技术FullStack:📚MERN/MEAN/MEVNStack|🌐Jamstack|🌍GraphQL|🔁RESTfulAPI|⚡WebSockets|🔄CI/CD|🌐Git&Versio

一篇让你使用vue-cli搭建SPA项目

目录一.Vue-CLi二.SAP项目三.为什么使用vue-cli搭建SPA项目四.SPA项目的搭建4.1.检查4.2.安装4.3.构建4.4.启动4.5.导入​编辑五.路由及嵌套使用5.1.路由5.2.嵌套好啦今天到这了,希望能帮到你!!!一.Vue-CLiVueCLI是一个基于Vue.js快速开发单页应用的脚手架工具

2023-09-21力扣每日一题

链接:2603.收集树中金币题意一个无向无根树,每个点上有Coins[T]个金币,每次你可以选择吸收离自己距离小于等于2的节点的所有金币,也可以移动到相邻格子上,求全部吸完最小的移动次数(任选起点,但吸完要返回起点)解:由于计算的是移动次数,所以你可以认为一直在吸,走到哪儿吸到哪儿计算最后移动次数很简单,由于要回到起点

【开发工具】idea 的全局搜索快捷键(Ctrl+shift+F)失效

文章目录前言1.取消输入法的快捷键(推荐使用)2.更改idea的快捷键3.热键占用总结前言当你发现在idea中看到用于全局搜索的快捷键就是Ctrl+shift+F,可是怎么按都不管用的时候,你就不要再执着于自己的操作继续狂点电脑按键了,因为可能根本就不是你的问题,而是微软的问题哟!!当我们在使用快捷键失效的时候,排除掉

24个Docker常见问题处理技巧

1.Docker迁移存储目录默认情况系统会将Docker容器存放在var/lib/docker目录下[问题起因]今天通过监控系统,发现公司其中一台服务器的磁盘快慢,随即上去看了下,发现/var/lib/docker这个目录特别大。由上述原因,我们都知道,在/var/lib/docker中存储的都是相关于容器的存储,所以

黑马头条 后端项目部署_持续集成 Jenkins配置

项目部署_持续集成1今日内容介绍1.1什么是持续集成持续集成(Continuousintegration,简称CI)指的是,频繁地(一天多次)将代码集成到主干持续集成的组成要素一个自动构建过程,从检出代码、编译构建、运行测试、结果记录、测试统计等都是自动完成的,无需人工干预。一个代码存储库,即需要版本控制软件来保障代码

【操作系统】聊聊进程间通信方式

作为操作系统软件治理的核心进程,那么进程间通信的方式就非常重要,常见的比如管道、消息队列、共享内存、信号量、信号、Socket等。本篇主要简单介绍下我们知道每个进程都有自己独立的用户空间,而内核空间是共享的。管道ps-ef|grepmysql其中的|就是一个管道符,只能进行单向传输数据。mkfifomyPipe创建一个

数字孪生技术如何提升工厂生产效率?

数字孪生技术是一项引领工业界数字化转型的创新力量。随着工业4.0时代的到来,制造业正经历着巨大的变革,数字孪生技术在这个变革中发挥了关键作用。它不仅仅是一种技术,更是一种理念,将现实世界与数字世界相结合,为工厂带来了前所未有的机遇和挑战。工厂数字化转型的目标是提高生产效率、降低成本、提升质量,并实现更灵活的生产。数字孪

记一次 .NET 某电力系统 内存暴涨分析

一:背景1.讲故事前些天有位朋友找到我,说他生产上的程序有内存暴涨情况,让我帮忙看下怎么回事,最简单粗暴的方法就是让朋友在内存暴涨的时候抓一个dump下来,看一看大概就知道咋回事了。二:Windbg分析1.到底是谁吃了内存这个问题说的再多也不为过,一定要看清楚这个程序是如何个性化发展的,可以使用!address-sum

课程学习成绩评分F2计算器

前言:由于大二的时候计算成绩太麻烦了,特别是综测评定小组还需要验证其他同学的是否正确,对于这种重复性高的工作,首先要想到的就是用计算机来实现,想起来上学期学了一点Java,就想简单的写一个程序。C++因为我们是知道他的公式的,所以就是输入数据就能够得到最终的结果。最开始我也不是直接就开始想到用Java来写的,最开始只是

热文推荐