[大话设计模式C++版] 第14章 老板回来,我不知道 —— 观察者模式

2023-11-14

源码可以在这里找到 大话设计模式C++版

双向耦合的代码

//Secretary.h 秘书类
#include <QList>

using namespace std;
class StockObserver;

class Secretary
{
private:
    QList<shared_ptr<StockObserver>> observers;
    QString action;

public:
    void Attach(shared_ptr<StockObserver> observer) {
        this->observers.append(observer);
    }
    void Notify() {
        for (int i = 0; i < this->observers.size(); i++) {
            this->observers[i]->Update();
        }
    }
    const QString &getAction() const {
        return action;
    }
    void setAction(const QString &newAction) {
        action = newAction;
    }
};
//StockObserver.h
#pragma execution_character_set("utf-8")
#include "Secretary.h"
#include <QDebug>

using namespace std;

class StockObserver
{
private:
    QString name;
    shared_ptr<Secretary> sub;

public:
    StockObserver(QString name, shared_ptr<Secretary> sub) {
        this->name = name;
        this->sub = sub;
    }
    void Update() {
        qDebug() << QString("%1 %2 关闭股票行情,继续工作!").arg(sub->getAction()).arg(name);
    }
};
//main.cpp
#pragma execution_character_set("utf-8")
#include "Secretary.h"
#include "StockObserver.h"

using namespace std;

int main(int argc, char *argv[])
{
    //前台小姐童子喆
    shared_ptr<Secretary> tongzizhe(new Secretary());
    //看股票的同时
    shared_ptr<StockObserver> tongshi1(new StockObserver("魏关姹", tongzizhe));
    shared_ptr<StockObserver> tongshi2(new StockObserver("易管查", tongzizhe));

    //前台记下了两位同时
    tongzizhe->Attach(tongshi1);
    tongzizhe->Attach(tongshi2);
    //发现老板回来
    tongzizhe->setAction("老板回来了!");
    //通知两个同事
    tongzizhe->Notify();

    return 0;
}

运行结果:

老板回来了! 魏关姹 关闭股票行情,继续工作!
老板回来了! 易管查 关闭股票行情,继续工作!

Secretary类StockObserver类 相互耦合,Secretary类 要增加观察者,StockObserver类 要前台的状态,如果需要增加NBA直播观察者,Secretary类 需要修改,违反了 开放-封闭原则。导致违 开放-封闭原则 的原因是没有遵守 依赖-倒转原则 ,我们应该让程序都依赖抽象,而不是相互依赖。

解耦实践一

//Observer.h 增加抽象观察者类
#include <QString>
#include <memory>

using namespace std;

class Secretary;

class Observer
{
protected:
    QString name;
    shared_ptr<Secretary> sub;
public:
    Observer(QString name, shared_ptr<Secretary> sub) {
        this->name = name;
        this->sub = sub;
    }
    virtual void Update() = 0;
};
//StockObserver.h 具体观察者
#pragma execution_character_set("utf-8")
#include "Observer.h"
#include "Secretary.h"
#include <QDebug>

using namespace std;

class StockObserver : public Observer
{
public:
    StockObserver(QString name, shared_ptr<Secretary> sub) : Observer(name, sub) {}
    void Update() {
        qDebug() << QString("%1 %2 关闭股票行情,继续工作!").arg(sub->getAction()).arg(name);
    }
};
//NBAObserver.h 具体观察者
#pragma execution_character_set("utf-8")
#include "Observer.h"
#include "Secretary.h"
#include <QDebug>

using namespace std;

class NBAObserver : public Observer
{
public:
    NBAObserver(QString name, shared_ptr<Secretary> sub) : Observer(name, sub) {}
    void Update() {
        qDebug() << QString("%1 %2 关闭NBA直播,继续工作!").arg(sub->getAction()).arg(name);
    }
};
//Secretary.h 前台类中的具体观察者类换成抽象观察者类
#include <QList>
#include "Observer.h"

using namespace std;

class Secretary
{
private:
    QList<shared_ptr<Observer>> observers;
    QString action;

public:
    void Attach(shared_ptr<Observer> observer) {
        this->observers.append(observer);
    }
    void Detach(shared_ptr<Observer> observer) {
        this->observers.removeOne(observer);
    }
    void Notify() {
        for (int i = 0; i < this->observers.size(); i++) {
            this->observers[i]->Update();
        }
    }
    const QString &getAction() const {
        return action;
    }
    void setAction(const QString &newAction) {
        action = newAction;
    }
};

StockObserver类NBAObserver类 具体观察者类与 Secretary类 具体被观察者类耦合,违反了 依赖-倒转原则 原则,被观察者类也需要做成抽象类

解耦实践二

//Subject.h 抽象被观察者(抽象通知者)
#include "Observer.h"

using namespace std;

class Subject
{
protected:
    QString action;
public:
    virtual void Attach(shared_ptr<Observer> observer) = 0;
    virtual void Detach(shared_ptr<Observer> observer) = 0;
    virtual void Notify() = 0;
    const QString &getAction() const {
        return action;
    }
    void setAction(const QString &newAction) {
        action = newAction;
    }
};
//Boss.h 具体通知者
#include <QList>
#include "Subject.h"
#include "Observer.h"

class Boss : public Subject
{
private:
    QList<shared_ptr<Observer>> observers;
    QString action;
public:
    virtual void Attach(shared_ptr<Observer> observer) {
        this->observers.append(observer);
    }
    virtual void Detach(shared_ptr<Observer> observer) {
        this->observers.removeOne(observer);
    }
    virtual void Notify() {
        for (int i = 0; i < this->observers.size(); i++) {
            this->observers[i]->Update();
        }
    }
};
//Observer.h 抽象观察者
#include <QString>
#include <memory>

using namespace std;

class Subject;

class Observer
{
protected:
    QString name;
    shared_ptr<Subject> sub;
public:
    Observer(QString name, shared_ptr<Subject> sub) {
    	this->name = name;
        this->sub = sub;
    }
    virtual void Update() = 0;
};
//StockObserver.h 看股票的同事
#pragma execution_character_set("utf-8")
#include "Observer.h"
#include "Subject.h"

using namespace std;

class StockObserver : public Observer
{
public:
    StockObserver(QString name, shared_ptr<Subject> sub) : Observer(name, sub) {}
    virtual void Update() override {
        qDebug() << QString("%1 %2 关闭股票行情,继续工作!").arg(sub->getAction()).arg(name);
    }
};
//main.cpp 客户端代码
#pragma execution_character_set("utf-8")
#include "Secretary.h"
#include "StockObserver.h"
#include "NBAObserver.h"
#include "Boss.h"

using namespace std;

int main(int argc, char *argv[])
{
    //老板胡汉三
    auto huhansan = shared_ptr<Boss>(new Boss());

    //看股票的同事
    auto tongshi1 = shared_ptr<StockObserver>(new StockObserver("魏关姹", huhansan));
    //看NBA的同事
    auto tongshi2 = shared_ptr<NBAObserver>(new NBAObserver("易管查", huhansan));

    huhansan->Attach(tongshi1);
    huhansan->Attach(tongshi2);

    huhansan->Detach(tongshi1);

    //老板回来
    huhansan->setAction("我胡汉三回来了!");
    //发出通知
    huhansan->Notify();

    return 0;
}

运行结果:

我胡汉三回来了! 易管查 关闭NBA直播,继续工作!

代码结构图:

在这里插入图片描述

Observer观察者模式 [李建忠C++笔记]

“组件协作”模式

  • 现代软件专业分工之后的第一个结果是“框架与应用程序的划分”,“组件协作”模式通过晚期绑定,来实现框架与应用程序之间的松耦合,是二者之间协作时常用的模式。
  • 典型模式
    • Template Method
    • Strategy
    • Observer / Event

动机(Motivation)

  • 在软件构建过程中,我们需要为某些对象建立一种“通知依赖关系”——一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知。如果这样的依赖关系过于紧密,将使软件不能很好地抵御变化。
  • 使用面向对象技术,可以将这种依赖关系弱化,并形成一种稳定的依赖关系。从而实现软件体系结构的松耦合。
//MainForm.h
class MainForm : public Form, public IProgress
{
private:
    TextBox* txtFilePath;
    TextBox* txtFileNumber;

    ProgressBar* progressBar;
	ConsoleNotifier cn;
public:
    void Button1_Click() {
        string filePath = txtFilePath->getText();
        int number = atoi(txtFileNumber->getText().c_str());
		
        FileSplitter splitter(filePath, number);
		splitter.addIProgress(this);
		splitter.addIProgress(&cn);
		
        splitter.split();
		splitter.removeIProgress(&cn);
    }
	
	void DoProgress(float value) {
		progressBar->setValue(value);
	}
};

class ConsoleNotifier : public IProgress
{
public:
	void DoProgress(float value) {
		cout << ".";
	}
};
//FileSplitter.h
class IProgress 
{
public:
	virtual void DoProgress(float value) = 0;
	virtual ~IProgress() {}
};

class FileSplitter
{
    string m_filePath;
    int m_fileNumber;
	
	//ProgressBar* m_progressBar;  //通知控件
	List<IProgress*> m_iprogressList;  //抽象通知机制,支持多个观察者
public:
    FileSplitter(const string& filePath, int fileNumber) 
		: m_filePath(filePath), m_fileNumber(fileNumber) {}
		
	void addIProgress(IProgress* iprogress) {
		m_iprogressVector.add(iprogress);
	}
	
	void removeIProgress(IProgress* iprogress) {
		m_iprogressVector.remove(iprogress);
	}
		
    void split() {
        //1.读取大文件
        
        //2.分批次向小文件中写入
        for (int i = 0; i < m_fileNumber; i++) {
            //...
			float progressValue = m_fileNumber;
			progressValue = (i+1) / progressValue;
			onProgress(progressValue);
        }
    }
	
protected:
	void onProgress(float value) {
		List<IProgress*>::Iterator itor = m_iprogressList.begin();
		
		//...
		while (itor != m_iprogressList.end()) {
			(*itor)->DoProgress(value);  //更新进度条
			itor++;
		}
	}
};

模式定义

定义对象间的一种一对多(变化)的依赖关系,以便当一个对象(Subject)的状态发生改变时,所有依赖与它的对象都得到通知并自动更新。——《设计模式》GoF

在这里插入图片描述

要点总结

  • 使用面向对象的抽象,Observer模式使得我们可以独立地改变目标与观察者,从而使二者之间的依赖关系达致松耦合。
  • 目标发送通知时,无需指定观察者,通知(可以携带通知信息作为参数)会自动传播。
  • 观察者自己决定是否需要订阅通知,目标对象对此一无所知。
  • Observer模式是基于事件的UI框架中非常常用的设计模式,也是MVC模式的一个重要组成部分。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

[大话设计模式C++版] 第14章 老板回来,我不知道 —— 观察者模式 的相关文章

  • 获取按下的按钮的返回值

    我有一个在特定事件中弹出的表单 它从数组中提取按钮并将标签值设置为特定值 因此 如果您要按下或单击此按钮 该函数应返回标签值 我怎样才能做到这一点 我如何知道点击了哪个按钮 此时代码返回 DialogResult 但我想从函数返回 Tag
  • 将数组向左或向右旋转一定数量的位置,复杂度为 o(n)

    我想编写一个程序 根据用户的输入 正 gt 负 include
  • 如何避免情绪低落?

    我有一个实现状态模式每个状态处理从事件队列获取的事件 根据State因此类有一个纯虚方法void handleEvent const Event 事件继承基础Event类 但每个事件都包含其可以是不同类型的数据 例如 int string
  • 如何在列表框项目之间画一条线

    我希望能够用水平线分隔列表框中的每个项目 这只是我用于绘制项目的一些代码 private void symptomsList DrawItem object sender System Windows Forms DrawItemEvent
  • 如何忽略“有符号和无符号整数表达式之间的比较”?

    谁能告诉我必须使用哪个标志才能使 gcc 忽略 有符号和无符号整数表达式之间的比较 警告消息 gcc Wno sign compare 但你确实应该修复它警告你的比较
  • 实时服务器上的 woff 字体 MIME 类型错误

    我有一个 asp net MVC 4 网站 我在其中使用 woff 字体 在 VS IIS 上运行时一切正常 然而 当我将 pate 上传到 1and1 托管 实时服务器 时 我得到以下信息 网络错误 404 未找到 http www co
  • WPF 中的调度程序和异步等待

    我正在尝试学习 WPF C 中的异步编程 但我陷入了异步编程和使用调度程序的困境 它们是不同的还是在相同的场景中使用 我愿意简短地回答这个问题 以免含糊不清 因为我知道我混淆了 WPF 中的概念和函数 但还不足以在功能上正确使用它 我在这里
  • 将目录压缩为单个文件的方法有哪些

    不知道怎么问 所以我会解释一下情况 我需要存储一些压缩文件 最初的想法是创建一个文件夹并存储所需数量的压缩文件 并创建一个文件来保存有关每个压缩文件的数据 但是 我不被允许创建许多文件 只能有一个 我决定创建一个压缩文件 其中包含有关进一步
  • C 预处理器库

    我的任务是开发源分析工具C程序 并且我需要在分析本身之前预处理代码 我想知道什么是最好的图书馆 我需要一些重量轻 便于携带的东西 与其推出自己的 为什么不使用cpp这是的一部分gcc suite http gcc gnu org onlin
  • 指针减法混乱

    当我们从另一个指针中减去一个指针时 差值不等于它们相距多少字节 而是等于它们相距多少个整数 如果指向整数 为什么这样 这个想法是你指向内存块 06 07 08 09 10 11 mem 18 24 17 53 7 14 data 如果你有i
  • 在 ASP.NET Core 3.1 中使用包含“System.Web.HttpContext”的旧项目

    我们有一些用 Net Framework编写的遗留项目 应该由由ASP NET Core3 1编写的API项目使用 问题是这些遗留项目正在使用 System Web HttpContext 您知道它不再存在于 net core 中 现在我们
  • vector 超出范围后不清除内存

    我遇到了以下问题 我不确定我是否错了或者它是一个非常奇怪的错误 我填充了一个巨大的字符串数组 并希望在某个点将其清除 这是一个最小的例子 include
  • 从路径中获取文件夹名称

    我有一些路c server folderName1 another name something another folder 我如何从那里提取最后一个文件夹名称 我尝试了几件事 但没有成功 我只是不想寻找最后的 然后就去休息了 Thank
  • 如何将单个 char 转换为 int [重复]

    这个问题在这里已经有答案了 我有一串数字 例如 123456789 我需要提取它们中的每一个以在计算中使用它们 我当然可以通过索引访问每个字符 但是如何将其转换为 int 我研究过 atoi 但它需要一个字符串作为参数 因此 我必须将每个字
  • Qt表格小部件,删除行的按钮

    我有一个 QTableWidget 对于所有行 我将一列的 setCellWidget 设置为按钮 我想将此按钮连接到删除该行的函数 我尝试了这段代码 它不起作用 因为如果我只是单击按钮 我不会将当前行设置为按钮的行 ui gt table
  • 将 unsigned char * (uint8_t *) 转换为 const char *

    我有一个带有 uint8 t 参数的函数 uint8 t ihex decode uint8 t in size t len uint8 t out uint8 t i hn ln for i 0 i lt len i 2 hn in i
  • mysql-connector-c++ - “get_driver_instance”不是“sql::mysql”的成员

    我是 C 的初学者 我认为学习的唯一方法就是接触一些代码 我正在尝试构建一个连接到 mysql 数据库的程序 我在 Linux 上使用 g 没有想法 我运行 make 这是我的错误 hello cpp 38 error get driver
  • 如何使用 std::string 将所有出现的一个字符替换为两个字符?

    有没有一种简单的方法来替换所有出现的 in a std string with 转义 a 中的所有斜杠std string 完成此操作的最简单方法可能是boost字符串算法库 http www boost org doc libs 1 46
  • ASP.NET MVC 6 (ASP.NET 5) 中的 Application_PreSendRequestHeaders 和 Application_BeginRequest

    如何在 ASP NET 5 MVC6 中使用这些方法 在 MVC5 中 我在 Global asax 中使用了它 现在呢 也许是入门班 protected void Application PreSendRequestHeaders obj
  • 防止索引超出范围错误

    我想编写对某些条件的检查 而不必使用 try catch 并且我想避免出现 Index Out of Range 错误的可能性 if array Element 0 Object Length gt 0 array Element 1 Ob

随机推荐

  • 【经验分享】- 这是一份来自 IT 男的电脑使用建议

    这是一份来自 IT 男的电脑使用建议 1 写在前面 2018 年高考结束我拿到了第一台笔记本电脑 此前对电脑接触地并不多 因此在这几年的电脑使用过程中积累了一些个人使用经验和使用技巧想要分享给可能还是电脑小白的你 个人一直以来用的还是 Wi
  • 自己动手定制Chromium系列之四:Chromium 58的一个编译配置

    aec untrusted delay for testing Current value from the default false From third party webrtc modules audio processing BU
  • (成功解决)Python连接clickhouse

    第一次尝试用Python连接clickhouse数据库 踩了不少坑 特此记录 帮助后人少犯错 运行环境 python 3 8 3 clickhouse driver 0 2 3 clickhouse sqlalchemy 0 2 0 sql
  • Linux-(C/C++)动态链接库生成以及使用(libxxx.so)

    linux静态库生成与使用 http www cnblogs com johnice archive 2013 01 17 2864319 html Linux中so文件为共享库 与windows下dll类似 不过实现要简单 so可以供多个
  • 小熊错误_小熊派4G开发板初体验

    开发板硬件资源介绍 前阵子小熊派发布了一款超高性价比的4G开发板 但是板子仅限量1000套 小熊派官方给我送了一块 我们一起来学习学习 板子做得小巧精致 控制核心用的是移远的EC100Y LTE Cat1无线通信模组 该模组可对所有用户开放
  • Python开发环境Wing IDE如何使用调试功能

    在使用Wing IDE开始调试的时候 需要设置断点的行 读取GetItemCount函数的返回 这可以通过单击行并选择Break工具栏条目 或通过单击行左边的黑色边缘 断点应该以实心红圈的形式出现 接下来使用绿色箭头图标开始调试或在Debu
  • 基于微信小程序的健身小助手小程序

    文末联系获取源码 开发语言 Java 框架 ssm JDK版本 JDK1 8 服务器 tomcat7 数据库 mysql 5 7 8 0 数据库工具 Navicat11 开发软件 eclipse myeclipse idea Maven包
  • Mysql中分组后取最新的一条数据

    在 SQL 中 你可以使用子查询和 ORDER BY 子句来实现按照特定字段进行分组 并获取每个分组中最新的一条记录 SELECT t1 FROM your table t1 INNER JOIN SELECT id MAX timesta
  • 云技术平台赋能媒体融合发展创新

    欢迎大家前往腾讯云技术社区 获取更多腾讯海量技术实践干货哦 作者 熊普江 媒体行业是传统而又新兴的行业 在数字化 信息化 移动化快速演进的今天 无论是用户 社会还是行业 政府都意识到 传统媒体与新兴媒体融合发展是必然之路 但媒体融合需要内容
  • 2021-07-14 React 代码规范整理

    文章目录 React 代码规范 1 基础规则 2 组件声明 1 组件名称和定义该组件的文件名称建议要保持一致 2 不要使用 displayName 属性来定义组件的名称 应该在 class 或者 function 关键字后面直接声明组件的名
  • 烟花代码

    div div
  • 日志-Log4J

    日志 程序中的日志可以用来记录程序在运行的时候点点滴滴 并可以进行永久存储 日志和输出语句的区别 输出语句 日志技术 取消日志 需要修改代码 灵活性比较差 不需要修改代码 灵活性比较好 输出位置 只能是控制台 可以将日志信息写入到文件或者数
  • R语言之 as.formula()

    今天学到一个东西 R 语言回归函数里面的公式函数 as formula 其作用就是将字符串转换成公式 gt aa ReadCount Age BMI Sex HAMD 1 1 Sex gt aa 1 ReadCount Age BMI Se
  • MFC之滑块与旋转控件23

    1 滑块 首先看看滑块相关的内容 从sitch看到 滑块分为五个内容 分别是矩形滑块位置 滑块左右两边的箭头 和矩形滑块分别距离左右两边的空间 1 先添加滑块控件和显示矩形滑块的位置编辑区 2 右击编辑区添加变量为int类型 3 添加滑块为
  • Qt实现不同项目的信号传递

    Qt实现windows通信 不同项目的窗口通信 有关Qt通信的知识 windows要实现不同项目窗口通信 需要用类似于windows h的api接口 数据传输主要通过 typedef struct tagCOPYDATASTRUCT cds
  • nowcoder-15165-字符串问题-kmp

    题目描述 有一个字符串 让你找到这个字符串 S 里面的子串T 这个子串 T 必须满足即使这个串的前缀 也是这个 串的后缀 并且 在字符串中也出现过一次的 提示 要求满足前后缀的同时也要在字符串中出现一次 只是前后缀可不行 输出最长满足要求字
  • Shell脚本进阶版合集

    文章目录 一 Linux监控服务端口脚本 二 Linux编译安装Nginx脚本 三 Linux监控一个主机状态脚本 四 Linux统计内存 CPU使用前十进程脚本 五 Linux 磁盘I O列长度监控脚本 六 Linux计算内存使用率占比
  • 【多元统计分析】09.独立性检验与正态性检验

    文章目录 九 独立性检验和正态性检验 1 独立性检验 2 一元数据正态性检验 3 多元数据的正态性检验 回顾总结 九 独立性检验和正态性检验 1 独立性检验 独立性检验 指的是将一个多元总体 X N p
  • 【cmake】find_package设置查找路径

    1 find package的作用与实例 用来查找第三方依赖包的 cmake文件 并根据 cmake文件生成依赖包的头文件目录和库文件路径等 CMakeLists txt实例 find package Protobuf REQUIRED i
  • [大话设计模式C++版] 第14章 老板回来,我不知道 —— 观察者模式

    源码可以在这里找到 大话设计模式C 版 双向耦合的代码 Secretary h 秘书类 include