详解 观察者模式

现实生活中,许多事物并不是独立存在的。让我们来看看几个例子

  1. 在双十一即将到来时,部分店铺会做活动,消费者会选择性消费。
  2. 用手机前多次输错密码,手机会显示在 N 秒后再输入密码,并返回最初状态。
  3. 当天气预报显示明日有雨,出门的用户就会记得带伞。

 

存在对象间一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新,这就是观察者模式(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()

 

其主要优点如下。

  1. 降低了监听者与观察者之间的耦合关系(抽象耦合关系)。

   由于监听者接口仅依赖于观察者接口,因此具体的监听者只知道是实现观察者接口的某个类的实例,但不需要知道具体是哪个类。同样,由于观察者仅仅依赖于监听者接口,因此具体观察者只知道是实现监听接口的某个类的实例,但不需要知道具体是哪个类。

2.目标与观察者之间建立了一套触发机制。


主要缺点如下。

1.如果监听者和观察者之间有循环依赖,观察会触发进行循环调用,可能导致系统崩溃。

2.当观察者对象很多时,通知会花费很多时间,程序效率降低

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值