【设计模式】观察者模式(改进后的)
前言
之前 观察者模式一篇 中,【通知者】需要知道【观察者】的类名和更新函数名,耦合度稍大,本篇稍稍修改一下,类似于大话设计模式里的C#描述的委托事件的方法。
采用 将类成员函数指针转换为普通指针 的方式,把每个【观察者】类的 相同原型的更新函数 指针转换为std::function然后保存起来。
然后在【通知者】的 通知函数 中调用保存起来的每个 【观察者】的更新函数。
本文去除了【观察者】的继承结构,【通知者】只需要知道每个【观察者】的更新函数原型。
去除了继承结构,因此每个【观察者】的更新函数也就不必是virtual的了。
描述
/************************************************************************/
/* 设计模式
观察者模式
本文介绍一种解耦的方法,使【通知者】不依赖于【观察者】
即【通知者】不知道【观察者】的太多细节,只需要知道【观察者】
的【更新函数原型】,所以这就要求【观察者】的更新函数的原型
要一致。
以【大话设计模式】里的讲到的Visual Studio的窗口变化为例:
当【启动调试】时,【输出】窗口展开,【反汇编】窗口展开
当【结束调试】时,【输出】窗口隐藏,【反汇编】窗口隐藏
所以这里的【通知者】为【调试器】,【观察者】为【输出】和【反汇编】窗口
/************************************************************************/
//调试器的两个行为:启动 和 停止
typedef enum _ESubjectState{Start = 0, End = 1}ESubjectState;
所需头文件
#include "stdafx.h"
#include <iostream>
#include <algorithm>
#include <vector>
#include <map>
#include <functional>
using namespace std;
PS:
说到头文件,不得不PS一下下,看头文件里的map,由于在用vector保存std::function时,不能使用std::find函数,也不能在for(iter)中使用if(*iter == std::function XX),因此我试图使用map来保证保存std::functiond唯一性,用map的find来查找待删除的观察者的更新函数。
是试图!!! 经过测试,发现还是不行!
在VC群里HB说因为std::function没有operator ==, 因此不能使用std::find函数, 因此也不能if(*iter == std::function XX),我想map可能是同样的原因。
两个观察者
观察者1:输出窗口类
//【观察者】--【输出窗口类】
class COutputWnd
{
public:
COutputWnd();
virtual ~COutputWnd();
void UpdateOutWnd(ESubjectState e); //更新状态
};
COutputWnd::COutputWnd(){}
COutputWnd::~COutputWnd(){}
void COutputWnd::UpdateOutWnd(ESubjectState e)
{
switch(e)
{
case Start:
cout<<"调试器启动了,展开 输出窗口 \n";
break;
case End:
cout<<"调试器关闭了,收起 输出窗口 \n";
default:
break;
}
}
观察者2:反汇编窗口类
//【观察者】--【反汇编窗口类】
class CDisassembling
{
public:
CDisassembling();
virtual ~CDisassembling();
void UpdateDisassembling(ESubjectState e);
};
CDisassembling::CDisassembling(){}
CDisassembling::~CDisassembling(){}
void CDisassembling::UpdateDisassembling(ESubjectState e)
{
switch(e)
{
case Start:
cout<<"调试器启动了,展开 反汇编窗口 \n";
break;
case End:
cout<<"调试器关闭了,收起 反汇编窗口 \n";
break;
default:
break;
}
}
通知者类
通知者抽象基类
//【通知者】抽象类
class CSubject
{
public:
typedef std::function<void(ESubjectState)> PUpdateFunc;
typedef vector<PUpdateFunc> PTRVEC;
CSubject();
virtual ~CSubject();
virtual void AddObserer(PUpdateFunc pFunc) = 0; //添加观察者
virtual void RemoveObserver(PUpdateFunc pFunc) = 0; //删除观察者
virtual void Notify(ESubjectState e) = 0; //通知函数
protected:
PTRVEC m_ptrArr;
};
CSubject::CSubject(){}
CSubject::~CSubject(){}
具体通知者类:调试器类
/**************************************
和前一篇将的在使用std::function时的问题类似,这里在函数AddObsererstd中使用std::find函数和RemoveObserver函数中使用std::find函数和opeartor == 时都有问题。
即暂时不能判断vector中没有待插入的函数时才插入, 也不能在删除观察者时查找到后再erase
***************************************/
//【通知者】--【调试器类】
class CDebugger : public CSubject
{
public:
CDebugger();
virtual ~CDebugger();
virtual void AddObserer(PUpdateFunc pFunc); //添加观察者
virtual void RemoveObserver(PUpdateFunc pFunc); //删除观察者
virtual void Notify(ESubjectState e); //通知函数
};
CDebugger::CDebugger(){}
CDebugger::~CDebugger(){}
//添加观察者,当待添加的观察者不存在时才添加
void CDebugger::AddObserer(PUpdateFunc pFunc)
{
//if(find(m_ptrArr.begin(), m_ptrArr.end(), pFunc) == m_ptrArr.end())
m_ptrArr.push_back(pFunc);
}
//删除观察者, 暂时不能删除
void CDebugger::RemoveObserver(PUpdateFunc pFunc)
{
/* PTRVEC::iterator iter = find(m_ptrArr.begin(), m_ptrArr.end(), pFunc);
if(m_ptrArr.end() != iter) m_ptrArr.erase(iter);*/
/*for (PTRVEC::iterator iter = m_ptrArr.begin(); iter != m_ptrArr.end(); ++ iter)
{
} */
}
void CDebugger::Notify(ESubjectState e)
{
for (PTRVEC::iterator iter = m_ptrArr.begin(); iter != m_ptrArr.end(); ++ iter)
{
(*iter)(e);
}
}
客户端代码
int _tmain(int argc, _TCHAR* argv[])
{
//通知者
CSubject* pSubject = new CDebugger();
//三个观察者, 一个是输出窗口, 另外两个是反汇编窗口
COutputWnd objOutPut;
CDisassembling objDisassembling1;
CDisassembling objDisassembling2;
cout<<"-----------添加三个观察者------------"<<endl;
CSubject::PUpdateFunc func1 = std::bind(&COutputWnd::UpdateOutWnd, &objOutPut, std::placeholders::_1);
pSubject->AddObserer(func1);
CSubject::PUpdateFunc func2 = std::bind(&CDisassembling::UpdateDisassembling, &objDisassembling1, std::placeholders::_1);
pSubject->AddObserer(func2);
CSubject::PUpdateFunc func3 = std::bind(&CDisassembling::UpdateDisassembling, &objDisassembling2, std::placeholders::_1);
pSubject->AddObserer(func3);
pSubject->Notify(Start);
cout<<endl;
pSubject->Notify(End);
cout<<endl<<"----------把观察者3删除-------------"<<endl;
pSubject->RemoveObserver(func3);
pSubject->Notify(Start);
cout<<endl;
pSubject->Notify(End);
cout<<endl<<endl;
delete pSubject; pSubject = NULL;
return 0;
}
运行结果
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端
· AI Agent开发,如何调用三方的API Function,是通过提示词来发起调用的吗