观察者模式,有时又称为发布-订阅模式,是软件设计模式的一种,在这个模式中,当被观察者发生改变时,会主动通知所有关注它的观察者,它的一个大致实现原理是:被观察者类中,有一个容器,当有观察者关注它时,被观察者会将这个观察者添加到这个容器中,被观察者有事件发生时,会遍历这个容器,通知所有关注它的观察者。
具体实现方法:被观察者发生事件的时候,如何通知观察者呢?通常做法是直接调用观察者的某个方法,但是这样做的坏处是,增加了类之间的耦合性,不利于以后程序的扩展。正确的做法是:不直接调用类,把观察者和被观察者都写一个基类,在基类里边调用,要使用的时候继承基类,实现具体的方法。
下面是我画的一个大概的框图,红色虚线就是数据的传递过程,也就是通知的触发过程
明白了一个大概的实现原理,下边直接上代码
#include "observer.h"
#include <iostream>
#include <set>
#include <string>
using namespace std;
//被观察者,先声明,因为观察者的类里边用到
class CObservable;
//观察者 这里定义一个纯虚基类,用来连接观察者和被观察者
class CObserver
{
public:
CObserver(){};
virtual ~CObserver(){};
virtual void Updata(CObservable * pObs,void * pArg=NULL)=0;//纯虚函数
};
//定义被观察者基类
class CObservable
{
public:
CObservable() :m_bChanged(false) {};
virtual ~CObservable() {};
void AddObs(CObserver *pObs);//添加观察者
void DelObs(CObserver *pObs);//删除观察者
void DelAllObs();
bool HasChanged();//查看是否有事件变化
void Notify(void *pArg=NULL);//事件触发
protected:
void SetChanged();//设置目标变化
void ClearChanged();//清除目标变化
private:
bool m_bChanged;
set<CObserver *> m_setObs;//保存观察者列表,当有事件发生的时候,遍历通知里边的观察者
};
//添加观察者
void CObservable::AddObs(CObserver *pObs)
{
if (!pObs) return;
m_setObs.insert(pObs);
}
//删除观察者
void CObservable::DelObs(CObserver * pObs)
{
if (!pObs) return;
m_setObs.erase(pObs);
}
//删除所有观察者
void CObservable::DelAllObs()
{
m_setObs.clear();
}
//通知观察者
void CObservable::Notify(void * pArg)
{
if (!HasChanged()) return;//如果没有变化,不用通知,直接返回
cout << "Notify Observer....";
set<CObserver*>::iterator itr = m_setObs.begin();//定义观察者的迭代器,遍历通知观察者集合里边的所有元素
for (; itr != m_setObs.end(); itr++)
{
(*itr)->Updata(this,pArg);//调用观察者的方法
}
}
bool CObservable::HasChanged()
{
return m_bChanged;
}
void CObservable::SetChanged()
{
m_bChanged = true;
}
void CObservable::ClearChanged()
{
m_bChanged = false;
}
//具体应用类
//定义一个被观察者
class CBlogs :public CObservable//继承被观察者基类
{
public :
void Publish(const string &strContend) {//定义自己的方法
cout << "blogs publish" << strContend << endl;
SetChanged();//有事件发生,给基类设置标志位
Notify(const_cast<char*>(strContend.c_str()));//调用通知观察者方法
}
};
//定义一个被观察者
class CPortal :public CObservable
{
public:
void Publish(const string &strContend) {
cout << "portal publish" << strContend << endl;
SetChanged();
Notify(const_cast<char*>(strContend.c_str()));
}
};
//定义观察者
class CRssreder :public CObserver
{
public:
CRssreder(const string &strName) :m_strName(strName) {}//设置观察者名称
virtual void Updata(CObservable * pObs, void * pArg=NULL) {//必须实现观察者基类的纯虚函数
char *pContent = static_cast<char*>(pArg);//转换参数
if (dynamic_cast<CBlogs*>(pObs)) {//判断是哪个被观察者触发的
cout <<m_strName<< "updata from CBlogs contend:" << pContent << endl;
}
if (dynamic_cast<CPortal*>(pObs)) {
cout << m_strName << "updata from CPortal contend:" << pContent << endl;
}
}
private:
string m_strName;
};
//使用
int main()
{
//定义被观察者
CBlogs * pBloger = new CBlogs();
CPortal * pPortal = new CPortal();
//定义观察者
CRssreder * pCRssReader = new CRssreder("rss reader");
pBloger->AddObs(pCRssReader);//注册观察者
pPortal->AddObs(pCRssReader);
pBloger->Publish("博客分享");//被观察者有事件发生
cout << endl;
pPortal->Publish("门户分享");
cout << endl;
system("pause");
return 0;
}
使用场景:通常被用来实现事件处理系统,因为被观察者里边定义的存放观察者的容器的类型是观察者基类,所以继承于这个观察者的类都可以调用,这样就大大增加了程序的灵活性
参考:https://baike.baidu.com/item/%E8%A7%82%E5%AF%9F%E8%80%85%E6%A8%A1%E5%BC%8F/5881786?fr=aladdin