现实生活中,许多事物并不是独立存在的。让我们来看看几个例子
- 在双十一即将到来时,部分店铺会做活动,消费者会选择性消费。
- 用手机前多次输错密码,手机会显示在 N 秒后再输入密码,并返回最初状态。
- 当天气预报显示明日有雨,出门的用户就会记得带伞。
存在对象间一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新,这就是观察者模式(Observer Pattern),观察者模式属于对象行为型模式。观察者模式完美的将观察者和被观察的对象分离开,在模块之间划定了清晰的界限,提高了应用程序的可维护性和重用性。
观察者模式
观察者模式有
1.监听者:处理事情
2.观察者:观察事件,通知监听者处理事件
观察者模式的流程:把监听者感兴趣的事件(包括事件的标识和类型)注册到观察者上,当事件到来以后,观察者通过注册表里面的事件标识和类型找到对应的监听者,通知每一个监听者处理事件
#include <iostream>
#include <functional>
#include <algorithm>
#include <string>
#include <vector>
#include <map>
using namespace std;
设计监听者(抽象)类
class Listener
{
public:
Listener(string s = " ") :str(s)
{}
//处理事件的接口(纯虚)
virtual void handleListenerMessage(int message)const = 0;
protected:
string str;//监听者唯一标识
};
class TestListener:public Listener
{
public:
TestListener(string name = " ") :Listener(name) {}
virtual void handleListenerMessage(int message)const
{
switch(message)
{
case 1:
cout << "1 recv" << endl;
break;
case 2:
cout << "2 recv" << endl;
break;
case 3:
cout << "3 recv" << endl;
break;
case 4:
cout << "4 recv" << endl;
break;
case 5:
cout << "5 recv" << endl;
break;
default:
cout << "refuse" << endl;
break;
}
}
};
设计观察者类
class Oberser
{
public:
//注册事件的接口,将监听者感兴趣的事件注册到 map 中
void RegisterMessage(const Listener* plistener,int iMessage)//Listener* plistener:监听者本身的标识 iMessage:感兴趣的事件
//看事件是否注册过,如果注册过,直接插入值;如果没有注册过,设计一个树的结构, pair 对象插入到 map 中
{
map<int,vector<const Listener*>>::iterator rit = mymap.find(iMessage);
if(rit == mymap.end())
{
vector<const Listener*> vec;
vec.push_back(plistener);
mymap[iMessage] = vec;
}
else
{
rit->second.push_back(plistener); //rit->first代表键,second代表值,值是容器类型,vector的对象,可以调用push方法
}
}
//通知接口,当事件到来时,通知感兴趣的监听者处理事件
void HandleMessage(int iMessage)
{
//第一步查找,为什么?
//1.事件注册了,监听到以后可能取消 2.外部传来的事件,没有任何监听者感兴趣,即事件没有注册,所以要查找
map<int,vector<const Listener*>>::iterator rit = mymap.find(iMessage);
if(rit == mymap.end())
{
cout << "no Listener instersting" << endl;
return;
}
//查找到以后通过 vector 迭代器遍历 vector 容器
vector<const Listener*>::iterator it = rit->second.begin();//怎样返回起始位置迭代器? rit指向当前位置迭代器,它的second代表vector对象,可以调用begin接口返回起始位置迭代器
for(it;it != rit->second.end();it++)
{
(*it)->handleListenerMessage(iMessage);//*it 代表容器里面的迭代器,const Listener*的指针,指针可以调用监听者处理的接口
//it 一解引用变成const Listener*的指针,相当于一个对象的指针指向监听者对象(常对象),handleListenerMessage是个普通方法,常对象无法调用普通方法,应给出一个常方法
}
}
private:
map<int,vector<const Listener*>> mymap;//监听者注册事件到观察者上,就要有一个注册表,用 map映射 实现一个键 对应 一个集合(集合里面有好多元素),另类实现一对多的关系
// int:事件类型 vector里面存放的是针对事件感兴趣的监听者
};
int main()
{
TestListener Listener1("Listener1");
TestListener Listener2("Listener2");
TestListener Listener3("Listener3");
Oberser ob;
ob.RegisterMessage(&Listener1,1);
ob.RegisterMessage(&Listener1,3);
ob.RegisterMessage(&Listener2,2);
ob.RegisterMessage(&Listener2,3);
ob.RegisterMessage(&Listener3,1);
ob.RegisterMessage(&Listener3,2);
ob.HandleMessage(1);//1事件到来以后,哪个监听者处理
ob.HandleMessage(4);//4事件没有在观察者里面注册
return 0;
}
首先要知道哪些监听者对哪些事件感兴趣,开始的时候怎样拿到这些信息,通过 register() 接口将监听者感兴趣的事件封装在容器中,一个事件可能有多个事件感兴趣,所以是一对多的映射关系,用 map 容器,一个事件对应多个感兴趣的监听者,将监听者放在一个集合中。
将事件和感兴趣的监听者注册到 map 后,监听事件通过 handleMessage() 接口接收事件并通知感兴趣的监听者处理事件。在监听者里面只要实现处理事件的接口即可。
假设现有 监听者注册事件 如下:
listener1 1,3(监听者1号 对 1事件和3事件 感兴趣)
listener2 2,3
listener3 1,2
当 1 号事件到来以后,传给观察者的 handleMessage() 接口,遍历整个 map 表,查找到对 1 号感兴趣的监听者,分别通知每个监听者处理事件,即调用 handleListenerMessage()
其主要优点如下。
- 降低了监听者与观察者之间的耦合关系(抽象耦合关系)。
由于监听者接口仅依赖于观察者接口,因此具体的监听者只知道是实现观察者接口的某个类的实例,但不需要知道具体是哪个类。同样,由于观察者仅仅依赖于监听者接口,因此具体观察者只知道是实现监听接口的某个类的实例,但不需要知道具体是哪个类。
2.目标与观察者之间建立了一套触发机制。
主要缺点如下。
1.如果监听者和观察者之间有循环依赖,观察者会触发进行循环调用,可能导致系统崩溃。
2.当观察者对象很多时,通知会花费很多时间,程序效率降低。