大话设计模式9—观察者模式(通知者与观察者)

2023-11-12

1.需求:老板回来,我不知道

假设同事1、同事2上班摸鱼炒股,为了防止老板发现,如果前台发觉老板回来迅速打电话告知两位同事。

2.双向耦合的设计

根据需求,可以设计两个类前台秘书类class Secretary和看股票同事类class StockObserver

2.1 前台秘书类

前台秘书类class Secretary的设计如下:有两个数据成员分别是存储着看股票同事类指针的数组,用于记录哪些同事需要前台帮忙观察老板是否回来;还需要一个字符串存储当前台发现老板回来打电话所说的内容。
那么前台秘书类的功能如下:①添加操作:添加需要帮忙的同事。②通知操作:通知数组中的同事。③设置和读取打电话的内容。

//Secretary.h
#ifndef SECRETARY_H
#define SECRETARY_H
#include<vector>
#include"StockObserver.h"
//前台秘书类
class Secretary {
private:
	vector<StockObserver*> m_observer;
	string m_action;
public:
	//Secretary();
	void Attach(StockObserver* observer);
	void Notify();
	void SetSecretaryAction(string action);
	string GetSecretaryAction();
};
#endif 

具体实现也比较简单,①添加操作:添加到vector。②通知操作:遍历数组,调用炒股同事的update方法(后续再提)。③设置和读取打电话的内容,也就是Set和Get m_action内容即可。

#include"Secretary.h"
#include"StockObserver.h"

//Secretary::Secretary() {
//	m_action = "老板没在";
//}

void Secretary::Attach(StockObserver* observer)
{
	m_observer.push_back(observer);
}

void Secretary::Notify()
{
	for (auto ob : m_observer)
	{
		ob->update();
	}
}
void Secretary::SetSecretaryAction(string action)
{
	m_action = action;
}

string Secretary::GetSecretaryAction()
{
	return m_action;
}

2.2 看股票同事类

看股票同事类class StockObserver的设计思路如下,需要有看股票同事的名字、前台秘书指针。构造函数负责初始化姓名和绑定前台秘书,上节中的update方法用来表示收到前台通知时需要采取的行动。
注意下这两个类属于互相引用,需要前置声明class Secretary;并在.cpp中实现其方法。

//StockObserver.h
#ifndef STOCKOBSERVER_H
#define STOCKOBSERVER_H
#include<string>
#include<iostream>
#include"Secretary.h"
using namespace std;
class Secretary;

//看股票同事类
class StockObserver {
private:
	string m_name;
	Secretary *m_sub;
public:
	StockObserver(string name, Secretary *sub);
	void update();
};
#endif // !STOCKOBSERVER_H

具体实现如下:update方法通过前台秘书的指针获取到前台电话内容,并执行关掉股票行情,继续工作。

#include"Secretary.h"
#include"StockObserver.h"
StockObserver::StockObserver(string name, Secretary *sub) :m_name(name), m_sub(sub){
}
void StockObserver::update()
{
	cout << m_sub->GetSecretaryAction()<<" "<<m_name ;
	cout << "关掉股票行情,继续工作" << endl;
}

2.3main函数及输出

最后main函数实现如下,在秘书类的对象中添加需要帮忙的同事1和同事2。当老板回来时将老板已回信息传递通知出去。

#include"Secretary.h"
#include"StockObserver.h"

int main()
{
	Secretary* qiantai = new Secretary();
	StockObserver *tongshi1 = new StockObserver("同事1", qiantai);
	StockObserver *tongshi2 = new StockObserver("同事2", qiantai);
	
	qiantai->Attach(tongshi1);
	qiantai->Attach(tongshi2);
	qiantai->SetSecretaryAction("老板已回");
	qiantai->Notify();
	return 0;
}

实现题设要求的内容。
在这里插入图片描述

3.解耦修改

分析上述实现,可以发现类的设计不够聪明,前台秘书类里面有装有炒股同事(观察者)的数组,而炒股同事又有前台秘书类的指针来获取前台的状态,属于双向耦合。此外,如果还有同事需要帮忙,但是并不是炒股,而是看NBA,那么不能只执行关掉股票,需要个性化定制update方法,故可以抽象出来观察者,不同的同事作为具体的观察者继承实现。此外,还可以让前台去除帮忙的同事。

3.1 抽象观察者类

依据分析需要定制化update方法,我们可以设计一个抽象Observer类,而将看股票的同事和看NBA 的同事作为其子类重写update方法。

//Observer.h
#ifndef OBSERVER_H
#define OBSERVER_H
#include<string>
#include<iostream>
#include"Secretary.h"
using namespace std;
class Secretary;
class Observer {
protected:
	string m_name;
	Secretary* m_sub;
public:
	Observer(string name, Secretary* sub);
	virtual void Update();
};

class StockObserver:public Observer{
public:
	StockObserver(string name, Secretary* sub);
	void Update();
};

class NBAObserver :public Observer {
public:
	NBAObserver(string name, Secretary* sub);
	void Update();
};
#endif

具体实现除了为不同的同事输出不同的执行动作外和上节相同。

//Observer.cpp
#include"Observer.h"
Observer::Observer(string name, Secretary* sub):m_name(name),m_sub(sub) {
}

StockObserver::StockObserver(string name, Secretary* sub) :Observer(name, sub) {
}
void StockObserver::Update() {
	cout << m_sub->GetSecretaryAction() << m_name;
	cout <<"关掉股票情况,继续工作" << endl;
}

NBAObserver::NBAObserver(string name, Secretary* sub) :Observer(name, sub) {
}
void NBAObserver::Update() {
	cout << m_sub->GetSecretaryAction() << m_name;
	cout << "关掉NBA比赛,继续工作" << endl;
}

3.2 前台秘书类

前台秘书类大体内容不变,只是增加一个删除功能,即某同事不需要帮忙,需要从数组中删除。

//Secretary.h
#ifndef SECRETARY_H
#define SECRETARY_H
#include<string>
#include<vector>
#include"Observer.h"
using namespace std;
class Observer;

//前台秘书类
class Secretary {
private:
	string m_action;
	vector<Observer*> m_observers;
public:
	void Attach(Observer* ob);
	void Detach(Observer* ob);
	void Notify();
	void SetSecretaryAction(string action);
	string GetSecretaryAction();
};
#endif 

实现如下,其中增加Attach、删除Detach针对抽象Observer类编程,减少了与具体类的耦合。

//Secretary.cpp
#include"Secretary.h"
void Secretary::Attach(Observer* ob) {
	auto pos = find(m_observers.begin(), m_observers.end(), ob);
	if (pos == m_observers.end())
	{
		m_observers.push_back(ob);
	}
}
void Secretary::Detach(Observer* ob) {
	auto pos = find(m_observers.begin(), m_observers.end(), ob);
	if (pos != m_observers.end())
	{
		m_observers.erase(pos);
	}
}
void Secretary::Notify() {
	for (auto ob : m_observers)
		ob->Update();
}
void Secretary::SetSecretaryAction(string action) {
	m_action = action;

}
string Secretary::GetSecretaryAction() {
	return m_action;
}

3.3 main函数

main函数中大体不变,先通知三个同事,再去掉一个通知。

#include"Observer.h"
#include"Secretary.h"
int main()
{
	Secretary* qiantai = new Secretary();
	StockObserver *tongshi1 = new StockObserver("同事1", qiantai);
	NBAObserver *tongshi2 = new NBAObserver("同事2", qiantai);
	NBAObserver *tongshi3 = new NBAObserver("同事3", qiantai);
	qiantai->Attach(tongshi1);
	qiantai->Attach(tongshi2);
	qiantai->Attach(tongshi3);
	qiantai->SetSecretaryAction("老板回来啦!!!");
	qiantai->Notify();
	cout << "........." << endl;
	qiantai->Detach(tongshi2);
	qiantai->Notify();
	return 0;
}

初步解耦,并完成其中的功能。
在这里插入图片描述

4.观察者模式实现

分析初步解耦代码的缺点:对于上述通知者来说,应该可以有多个通知者存在,试想如果前台没来得及通知观察者老板回来了,那么发出通知的可能是老板,也可能是其他人而且发出通知的内容也不一样。所以可以抽象出来一个通知者,而老板、前台都是继承他的具体通知者,这样观察者就可以不依赖具体实现,达到解耦。
在这里插入图片描述

此外,如下图在实例化观察者同事1、同事2 的时候,构造函数直接绑定了前台作为其通知者,所以,我们考虑在构造函数中暂不绑定谁作为通知者,新增设置通知者SetNotifier()方法来绑定通知者。

在这里插入图片描述

4.1 通知者

抽象通知者和具体通知者实际并没有任何区别,其增加、删除观察者,通知等方法完全相同,只是具体通知者需要通知的对象数组可能不同,所以,抽象观察者和上节前台秘书类相同,具体通知者boss和Secretary 继承就可以。

//Notifier.h
#ifndef NOTIFIER_H
#define NOTIFIER_H
#include<string>
#include<vector>
#include"Observer.h"
using namespace std;
class Observer;

//抽象通知者
class Notifier {
protected:
	string m_action;
	vector<Observer*> m_observers;
public:
	void Attach(Observer* ob);
	void Detach(Observer* ob);
	void Notify() ;
	void SetNotifierAction(string action);
	string GetNotifierAction();
	virtual ~Notifier() {};
};

class Boss :public Notifier {
};

class Secretary :public Notifier {
};
#endif 
//Notifier.cpp
#include"Notifier.h"
void Notifier::Attach(Observer* ob) {
	auto pos = find(m_observers.begin(), m_observers.end(), ob);
	if (pos == m_observers.end())
	{
		m_observers.push_back(ob);
	}
}
void Notifier::Detach(Observer* ob) {
	auto pos = find(m_observers.begin(), m_observers.end(), ob);
	if (pos != m_observers.end())
	{
		m_observers.erase(pos);
	}
}
void Notifier::Notify() {
	for (auto ob : m_observers)
		ob->Update();
}
void Notifier::SetNotifierAction(string action) {
	m_action = action;

}
string Notifier::GetNotifierAction() {
	return m_action;
}

4.2 观察者

观察者上一节已经解耦了,抽象出来一个观察者,让看NBA的和炒股的同事继承它,本节沿用上一节内容,只是将构造函数只初始化姓名。

//Observer.h
#ifndef OBSERVER_H
#define OBSERVER_H
#include<string>
#include<iostream>
#include"Notifier.h"
using namespace std;
class Notifier;
class Observer {
protected:
	string m_name;
	Notifier* m_sub;
public:
	Observer(string name);
	virtual void Update()=0;
	void SetNotifier(Notifier* sub);
};

class StockObserver:public Observer{
public:
	StockObserver(string name);
	void Update();
};

class NBAObserver :public Observer {
public:
	NBAObserver(string name);
	void Update();
};
#endif
//Observer.cpp
#include"Observer.h"

Observer::Observer(string name):m_name(name) {
}

void Observer::Update() {
}

void Observer::SetNotifier(Notifier* sub) {
	m_sub = sub;
}

StockObserver::StockObserver(string name) :Observer(name) {
}
void StockObserver::Update() {
	cout << m_sub->GetNotifierAction() << m_name;
	cout <<"关掉股票情况,继续工作" << endl;
}

NBAObserver::NBAObserver(string name) :Observer(name) {
}
void NBAObserver::Update() {
	cout << m_sub->GetNotifierAction() << m_name;
	cout << "关掉NBA比赛,继续工作" << endl;
}

4.3 main函数

#include"Observer.h"
#include"Notifier.h"
int main()
{
	//实例化第一个通知者前台
	Secretary* qiantai = new Secretary();
	//实例化三个观察者,两个看NBA、一个炒股
	StockObserver*tongshi1 = new StockObserver("同事1");
	NBAObserver*tongshi2 = new NBAObserver("同事2");
	NBAObserver*tongshi3 = new NBAObserver("同事3");
	//通知者将需要帮忙的同事添加到数组中
	qiantai->Attach(tongshi1);
	qiantai->Attach(tongshi2);
	qiantai->Attach(tongshi3);
	//观察者也绑定前台
	tongshi1->SetNotifier(qiantai);
	tongshi2->SetNotifier(qiantai);
	tongshi3->SetNotifier(qiantai);
	//通知者打电话,告诉老板回来了
	qiantai->SetNotifierAction("咱老板回来啦!!!");
	qiantai->Notify();
	//通知者去掉同事2再通知老板回来了
	qiantai->Detach(tongshi2);
	qiantai->Notify();
	
	return 0;

在这里插入图片描述
再看看如果老板通知员工是怎么样的,需要绑定老板

#include"Observer.h"
#include"Notifier.h"
int main()
{
	//实例化第一个通知者前台
	Secretary* qiantai = new Secretary();
	//实例化三个观察者,两个看NBA、一个炒股
	StockObserver *tongshi1 = new StockObserver("同事1");
	NBAObserver *tongshi2 = new NBAObserver("同事2");
	NBAObserver *tongshi3 = new NBAObserver("同事3");
	//通知者将需要帮忙的同事添加到数组中
	qiantai->Attach(tongshi1);
	qiantai->Attach(tongshi2);
	qiantai->Attach(tongshi3);
	//观察者也绑定前台
	tongshi1->SetNotifier(qiantai);
	tongshi2->SetNotifier(qiantai);
	tongshi3->SetNotifier(qiantai);
	//通知者打电话,告诉老板回来了
	qiantai->SetNotifierAction("咱老板回来啦!!!");
	qiantai->Notify();
	//通知者去掉同事2再通知老板回来了
	qiantai->Detach(tongshi2);
	qiantai->Notify();


	//老板通知
	cout << "........" << endl;
	Boss* boss = new Boss();
	boss->Attach(tongshi1);
	boss->Attach(tongshi2);
	boss->Attach(tongshi3);
	tongshi1->SetNotifier(boss);
	tongshi2->SetNotifier(boss);
	tongshi3->SetNotifier(boss);

	boss->SetNotifierAction("让俺看看谁没工作!!!");
	boss->Notify();
	boss->Detach(tongshi1);
	boss->Notify();

	return 0;
}

在这里插入图片描述
完成了题设的要求。

5.观察者模式

观察者模式:定义了一种一对多的依赖关系,让多个观察者对象同时监听某一主题对象,当这个主题对象状态发生改变时会通知所有观察者对象,使他们能够自动更新自己。
如下图所示,
Subject类是抽象通知者,将所有观察者的引用保存在数组中,并提供删除增加观察者对象。
Oberserver类是抽象观察者,在得到主题通知时更新自己,接口中有update方法交给子类实现
ConcreteSubject是具体通知者
ConcreteOberserver是具体观察者,保存一个指向具体主题对象的引用,实现抽象观察者要求的更新接口。

在这里插入图片描述
观察者模式所作的工作就是解耦合,让耦合的双方都依赖于抽象,从而使得各自的变化都不会影响另一边变化。使用时机:当一个对象改变需要同时改变其他对象时②

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

大话设计模式9—观察者模式(通知者与观察者) 的相关文章

  • 基于SpringBoot+MyBatis 五子棋双人对战

    1 核心功能 2 演示效果 3 创建项目 4 数据库设计 5 配置文件 6 用户模块 6 1 登录实现 6 1 1 前后端交互接口 6 1 2 model 层 6 1 3 mapper 层 6 1 4 xml 层 6 1 5 service
  • 谷歌浏览器添加.crx插件

    1 美图 1 下载 crx格式的插件 常用插件网址 http www cnplugins com http chromecj com 2 更改文件后缀名为zip 3 解压zip文件 不可直接双击解压 直接解压可能使解压后的文件不全 使用命令
  • 论文写作的基本套路

    最近在写论文 写好之后给大神师兄看了看 提出了一些意见 按照师兄的意见整理出来 以供以后写作参考 博主是写的英文期刊论文 一 Abstract 一篇论文的精华都在abstract中 一片论文是否能够抓住审稿人的眼球 让审稿人有兴趣读下去 a
  • Vijava (更改虚拟机配置信息(内存、CPU和添加新磁盘)终极版本)

    package com vmware server import java util List import java util Map import org apache log4j FileAppender import org apa
  • 解决idea不显示Services工具栏的问题

    Idea的Services窗口可以展示项目的入口引导类及端口 可以方便的启动及停止相关服务 假如自己的idea里边不显示 可以通过一下方式解决 1 找到自己 idea工作空间的workspace xml文件 2 文件内搜索 RunDashb
  • vue项目中封装手动上传多个图片并支持修改和移除

    现有的组件库无法满足手动上传文件到服务器 并支持通过按钮修改和移除文件的操作 所以我利用原生input进行封装 如有需要请拿走 1 页面部分 div class container div class upload picture div
  • centos7 查看服务器配置信息

    1 linux查看版本当前操作系统发行信息 cat etc centos release cat etc centos release 2 查看内核版本uname a或者cat proc version 3 查看CPU参数 1 查看 CPU
  • uniapp 本地缓存剩余时间

    封装代码 在项目根目录项新建 utils 文件夹 再新建一个 idea js 文件 import store from store js module exports 计算剩余时间 timeRemaining val time return
  • 二叉搜索树-AVL树的实现

    首先 AVL树是一棵加了额外平衡条件的搜索树 这是因为普通的搜索树如果插入的key接近有序的话 二叉树将会退化成一个单链表 导致查找的时间复杂度为O N 而AVL树中用一个平衡因子来制约树的左右子树的高度 保证任何节点的左右子树高度之差最多
  • Android开发人员应该选择哪种语言?

    自 Google 于 2017 年宣布 Kotlin 成为 Google IO 的 Android 开发官方语言以来 想要成为Android开发人员的程序员正陷入两难境地 在讨论这个问题前 我首先要明确一点 不要陷入编程语言战争 不要进行语
  • 【亲测解决】AttributeError: module ‘tensorflow‘ has no attribute ‘__version__‘

    今天在安装使用bert as service时报错 报错信息如下 AttributeError module tensorflow has no attribute version 一看 懵逼了 啥 tensorflow么有 version
  • centos 内核升级

    首先查看centos版本 cat etc centos release 或者 rpm q centos release 查看内核版本 uname sr 查看官方内核 https www kernel org 接下来升级内核 大多数现代发行版
  • 太牛叉了!解决“卡脖子”的国产自主 IDE [狗头.jpg]

    推荐关注 综合整理 程序员的那些事 ID iProgrammer 解决 卡脖子 的自主创新 IDE 最近有一个的国产自主创新的 CEC IDE 震动了程序员圈子 在 CEC IDE 官网简介中的 安全可控 条目自称 国企品牌 自主研发 注意
  • k8s运维 pod、node、namespace、pv处于terminating的原因及处理方法

    1 概述 node pod ns pv由于一些原因在生产中处于terminating的状态 常规方法无法删除 一下总结了一些原因以及删除方法 2 node处于Terminating状态原因及处理方法 node节点不可达的情况下 kubect
  • MATLAB嵌套循环求解1到1000的素数和

    熬夜打卡 代码都跑过一遍的 没有任何问题啦 方法一 matlab的嵌套循环 重在理解 clc clear s 0 for i 2 1000 for j 2 32 if mod i j break end end if j gt i j s
  • 【华为OD机试】阿里巴巴找黄金宝箱(IV)(C++ Python Java)2023 B卷

    时间限制 C C 1秒 其他语言 2秒 空间限制 C C 262144K 其他语言524288K 64bit IO Format lld 题目描述 一贫如洗的樵夫阿里巴巴在去砍柴的路上 无意中发现了强盗集团的藏宝地 藏宝地有编号从0 N的箱

随机推荐

  • 常用的BOM属性 - Kaiqisan

    终于出狱了 今天重新恢复博客的更新 大致谈谈我复习面试上面的查漏补缺的内容 首先讲讲什么是BOM BOM简单来说就是浏览器对象 只有js在浏览器环境运行时才会被赋予的对象 location对象 该对象内所有的属性都与URL有关 常常用于提取
  • 攻防世界————fileclude(内含php伪协议菜鸟讲解)

    先进去发现为一坨php代码 新手勉勉强强看得懂 接下来我们分析代码 WRONG WAY
  • Angular2-使用Augury来调试Angular2程序

    原文链接 http www jianshu com p efecaea287f2 推荐 Augury Angular专用的chrome 调试插件 如题 就在前几天的2016 12 8谷歌开发者大会上 angular2的leader来给我们演
  • idea字体主题集合

    http color themes com view index
  • 意念控制四旋翼 学习笔记

    第一部分 模块原始数据 拿到模块 在网上查了一圈 发现基本没什么有用的资料 很多都是一些相关但是没有实际价值的东西 许多论文都是再谈怎么去做 而没有实实在在的去完成这么一个过程 废话不多说 直接步入正题 昨天在网上才发现这个软件 据评论说是
  • 最近大火的「元宇宙」是什么?

    公众号后台回复 图书 了解更多号主新书内容 作者 腾讯技术工程特约撰稿人 李佳华 本文将介绍元宇宙的由来和底层技术 探讨海内外资本在这条赛道上的布局 元宇宙将会对哪些行业产生变革的影响 这些影响背后凸显了元宇宙的哪些价值 以及元宇宙逐步实现
  • openwrt reboot流程

    openwrt 系统中 当执行了 reboot 命令 系统将会发生什么事情呢 如何进行重启的呢 下面来一起看一下 reboot 应用层操作 首先 reboot 是由busybox 它是一个集成了常用Linux命令和工具的软件 提供的一个Li
  • leetcode算法面试题:串联所有单词的子串问题、单词拆分问题

    串联所有单词的子串问题 给定一个字符串 s 和一些 长度相同 的单词 words 找出 s 中恰好可以由 words 中所有单词串联形成的子串的起始位置 注意子串要与 words 中的单词完全匹配 中间不能有其他字符 但不需要考虑 word
  • 数据挖掘算法基础-关联规则

    数据挖掘中 被常拿来说的啤酒尿布的例子就是一个很典型的运用关联算法来做购物来分析的例子 常被用于交易数据 关系数据的分析 发现数据集中隐藏的频繁模式 这些频繁模式可以用关联规则的形式表示 有效的关联规则对商家的商品进出货摆放都有很大的指导意
  • 直方图均衡化与直方图规定化

    一 认识图像 当我们面对图像的时候 我们面对的是抽象的矩阵 如下图 下面是0 255的灰度图像的表示 密密麻麻的 那么我们做的直方图 其实就是对这些像素值的统计 如下图所示 其中Bin表示条数 数据和范围是对图的解释 二 为什么要做直方图均
  • qt 嵌入web页面_Qt -在应用程序中嵌入Web内容之环境搭建

    一 Qt应用程序与Web结合的发展 1 从Qt5 5开始 Qt WebKit模块被废弃了 取而代之的是Qt WebEngine模块 当时可以使用该模块将应用程序与Web技术结合 2 Qt WebEngine模块提供了一个Web浏览器引擎 可
  • ChatGPT:概述Vue.js中data函数初始化和created钩子函数调用的顺序和问题解决方法

    ChatGPT 概述Vue js中data函数初始化和created钩子函数调用的顺序和问题解决方法 我将输入一段Vue代码 请你记住 created console log this queryInfo this getClueList
  • Libuv源码分析 —— 6. 事件循环【uv_run】

    通过之前的学习 咱们已经明白了在事件循环中的三个核心内容 分别是 Libuv源码分析 定时器 Libuv源码分析 idle prepare check Libuv源码分析 poll io 现在让咱们从头捋一遍事件循环到底完成了什么功能呢 u
  • scrapy里面的response.xpath(“用xpath插件找打的路径“)返回值为空?

    response xpath 用xpath插件找打的路径 返回值为空 1 可能是因为路径是有问题的 2 可能是start urls的路径是有问题的 可以从network中找找路径 复制一下
  • 使用vant2问题整理

    1 export createVNode imported as createVNode was not found in vue possible exports EffectScope computed customRef defaul
  • C++11移动语义解析

    当给函数传递对象当做函数参数时 可以使用引用类型来减少拷贝对象的代价 尤其是避免容器的拷贝等 但是当把函数内的局部对象当做返回值时 我们无法返回该局部对象的引用 导致每次返回局部对象都会进行拷贝 因为返回局部对象的引用是无意义的 当函数调用
  • 编译原理实验日志

    编译原理 生成四元式 实验原理 构造SLR 1 分析表 调试过程 实验原理 构造SLR 1 分析表 首先求得follow集 follow E follow T follow F 画出DFA状态转换图 调试过程 没有判断 因为字符串中没有表示
  • dubbo优雅停机

    dubbo优雅停机 Dubbo是通过JDK的ShutdownHook来完成优雅停机的 所以如果用户使用 kill 9 PID 等强制关闭指令 是不会执行优雅停机的 只有通过 kill PID 时 才会执行 原理 服务提供方 停止时 先标记为
  • grafana与prometheus实现监控可视化

    1 Grafana基础知识 Grafana是一个开源的指标监测和可视化工具 官方网站为 Grafana The open observability platform Grafana Labs 常用于展示基础设施的时序数据和应用程序运行分析
  • 大话设计模式9—观察者模式(通知者与观察者)

    大话设计模式9 观察者模式 老板回来 我不知道 1 需求 老板回来 我不知道 2 双向耦合的设计 2 1 前台秘书类 2 2 看股票同事类 2 3main函数及输出 3 解耦修改 3 1 抽象观察者类 3 2 前台秘书类 3 3 main函