[C++实现 设计模式(13)] : 观察者模式

16 篇文章 0 订阅

情景描述

       法家的代表人物除了韩非子,还有一个非常重要的代表人物–李斯.李斯成为最强诸侯国秦国的上尉,致力与统一全国,于是安插了间谍到各个国家的重要人物的身边,以获取必要的信息.韩非子作为韩国的重量级人物,身边必然有不少间谍.韩非子做的事,李斯都了如指掌.
按照如下类图我们可以看到 :
在这里插入图片描述

  • Observable
    实现该接口的都是被观察者,那韩非子是被观察者,他当然也要实现该接口了,同时他还有与其他庸人相异的事要做,因此他还是要实现IHanFeizi接口 .
  • Observer
    这样显得更抽象化,所有实现该接口的都是观察者(类似李斯这样的)。

       Observable是被观察者,就是类似韩非子这样的人,在Observable接 口中有三个比较重要的方法,分别是addObserver增加观察者,deleteObserver删除观察者,notifyObservers 通知所有的观察者,这是什么意思呢?我这里有一个信息,一个对象,我可以允许有多个对象来察看,你观察也成,我观察也成,只要是观察者就成,也就是说我的改变或动作执行,会通知其他的对象.

这样创建的好处是为了解决以下两个问题 :

  • 问题1: 占满CPU(比如使用while(true)), 要投入的硬件太多,还不让别人的程序运行.一台服务器只能跑你这一个程序.
  • 问题2: 被监控者(韩非子)不是只被一个人监控 ; 而且监控者(李斯)要看到被监控者(韩非子)的一些列活动 , 被监控者(韩非子)的一系列活动都要通知到监控者(李斯).

代码如下 :

#include<iostream>
#include<string>
#include<vector>
//#include<vld.h>

using namespace std;

//观察者模式
#if 0
//被观察者接口
class IHanFeiZi
{
public:
	//韩非子也是人 , 也要吃早饭的
	virtual void haveBreakfirst() = 0;
	//韩非子也是人 , 是人就要娱乐活动
	virtual void haveFun() = 0;
};

//观察者接口
class Observer
{
public:
	//一发现别人有动静 , 自己也要行动起来
	virtual void update(string context) = 0;
};

//被观察接口
class Obsevable
{
public:
	//增加一个观察者
	virtual void addObserver(Observer* observer) = 0;
	//删除一个观察者
	virtual void deleteObserver() = 0;
	//既然要观察 , 我发生改变了他也应该有所动作 , 通知观察者
	virtual void notifyObserver(string context) = 0;
};

//被观察者实现类
class HanFeiZi : public Obsevable , IHanFeiZi
{
public:
	//增加观察者
	void addObserver(Observer* obs)
	{
		_observerVec.push_back(obs);
	}

	//删除观察者(只是形似 , 并不具体实现)
	void deleteObserver()
	{
		_observerVec.pop_back();
	}

	//通知所有的观察者
	void notifyObserver(string context)
	{
		int len = _observerVec.size();
		for(int i = 0 ; i < len ;++i)
		{
			_observerVec[i]->update(context);
		}
	}

	//韩非子要吃饭了
	void haveBreakfirst()
	{
		cout<<"韩非子 : 开始吃饭了..."<<endl;
		notifyObserver("韩非子在吃饭");
	}
	//韩非子开始娱乐了
	void haveFun()
	{
		cout<<"韩非子 : 开始娱乐了..."<<endl;
		notifyObserver("韩非子在娱乐");
	}

private:
	vector<Observer*> _observerVec;
};

//具体的观察者
class LiSi : public Observer
{
public:
	//首先李斯是个观察者 , 一旦韩非子有活动 , 他就知道 , 他就要向老板汇报
	void update(string str)
	{
		cout<<"李斯 : 观察到韩非子有活动 , 开始向老板汇报..."<<endl;
		reportToQinShiHuang(str);
		cout<<"李斯 : 汇报完毕..."<<endl;
	}
private:
	//汇报给秦始皇
	void reportToQinShiHuang(string str)
	{
		cout<<"李斯 : 报告 , 秦老板! 韩非子有活动了--->"+ str<<endl;
	}
};

class WangSi : public Observer
{
public:
	//王斯 , 看到韩非子有活动
	void update(string str)
	{
		cout<<"王斯 : 观察到韩非子有活动 , 自己也开始活动了..."<<endl;
		cry(str);
		cout<<"王斯 : 哭死了..."<<endl;
	}
private:
	//一看到韩非子有活动 , 他就哭
	void cry(string str)
	{
		cout<<"王斯 : 因为"+ str+"--所以我悲伤..."<<endl;
	}
};

class LiuSi : public Observer
{
public:
	//刘斯 , 看到韩非子有活动
	void update(string str)
	{
		cout<<"刘斯 : 观察到韩非子有活动 , 开始活动了..."<<endl;
		happy(str);
		cout<<"刘斯 : 乐死了..."<<endl;
	}
private:
	//一看到韩非子有活动 , 他就快乐
	void happy(string str)
	{
		cout<<"刘斯 : 因为"+ str+"--所以我快乐..."<<endl;
	}
};


int main()
{
	//三个观察者产生出来
	Observer* lisi = new LiSi();
	Observer* wangsi  = new WangSi();
	Observer* liusi = new LiuSi();

	//定义出韩非子
	HanFeiZi* hanfeizi = new HanFeiZi();

	//我们后人根据历史 , 描述这个场景 , 有三个人在观察韩非子
	hanfeizi->addObserver(lisi);
	hanfeizi->addObserver(wangsi);
	hanfeizi->addObserver(liusi);

	//然后这里我们看看韩非子在干什么
	hanfeizi->haveBreakfirst();

	delete lisi ; delete wangsi ; delete liusi ;
	delete hanfeizi;


	return 0;
}

#endif

运行结果 :
在这里插入图片描述

观察者模式的定义

定义 : 观察者模式也叫做发布订阅模式 ; 定义对象间一种一对多的依赖关系 , 使得当每一个对象改变状态 , 则所有依赖于它的对象都会得到通知并被自动更新 .

观察者模式通用类图 :
在这里插入图片描述

观察者模式的结构 :

  • Subject被观察者
    定义被观察者必须实现的职责 , 它必须能够动态的增加 , 取消观察者 . 它一般是抽象类或者是实现类 , 仅仅完成作为被观察者必须实现的职责 : 管理观察者并通知观察者.
  • Observer观察者
    观察者接受到消息后 , 即进行update(更新方法)操作 , 对接收到的信息进行处理.
  • ConcreteSubject具体的被观察者
    定义被观察者自己的业务逻辑 , 同时定义对哪些事件进行通知 .
  • ConcreteObserver被观察者
    每个观察者在接收到消息后的处理反应是不同的 , 各个观察者有自己的处理逻辑 .

观察者模式的应用

观察者模式的优点
  • 观察者和被观察者之间是抽象耦合
    如此设计,则不管是增加观察者还是被观察者都非常容易扩展,而且在C++中都已经实现的抽象层级的定义,在系统扩展方面更是得心应手 .
  • 建立一套触发机制
    根据单一职责原则,每个类的职责是单一的,那么怎么把各个单一的职责串联成真实世界的复杂的逻辑关系呢?比如,我们去打猎,打死了一只母鹿,母鹿有三个幼崽,因失去了母鹿而饿死 , 尸体又被两只秃鹰争抢 , 因分配不均匀 , 秃鹰开始斗殴,然后赢弱的充鹰死掉,生存下来的秃鹰,则因此扩大了地盘…这就是触发机制,形成了一个触发链 . 观察者模式可以完美地实现这里的链条形式 .
观察者模式的缺点

       观察者模式需要考虑一下开发效率和运行效率问题 , 一个被观察者,多个观察者,开发和协调就会比较复杂 , 而且消息的通知默认是顺序执行 , 一个观察者卡壳,会影响整体的执行效率 . 在这种情况下,一般考虑采用异步的方式 .
多级触发时的效率更是让人担忧,大家在设计时注意考虑 .

观察者模式的使用场景
  • 关联行为场景。需要注意的是,关联行为是可拆分的而不是“组合”关系 .
  • 事件多级触发场景 .
  • 跨系统的消息交换场景,如消息队列的处理机制 .
观察者模式的注意事项

使用观察者模式也有以下两个重点问题要解决 :

  • 广播链的问题
    如果你做过数据库的触发器,你就应该知道有一个触发器链的问题,比如表A上写了一个触发器,内容是一个字段更新后更新表B的一条数据, 而表B上也有个触发器,要更新表C,表C也有触发器…完蛋了,这个数据库基本上就毁掉了!我们的观察者模式也是一样的问题,一个观察者可以有双重身份,既是观察者,也是被观察者,这没什么问题呀,但是链一旦建立,这个逻辑就比较复杂,可维护性非常差,根据经验建议,在一个观察者模式中最多出现一个对象既是观察者也是被观察者,也就是说消息最多转发一次(传递两次),这还是比较好控制的。

        注意 : 它和责任链模式的最大区别就是观察者广播链在传播的过程中消息是随时更改的,它是由相邻的两个节点协商的消息结构 ; 而责任链模式在消息传递过程中基本上保持消息不可变,如果要改变,也只是在原有的消息上进行修正 .

  • 异步处理问题
    被观察者发生动作了,观察者要做出回应,如果观察者比较多,而且处理时间比较长怎么办?那就用异步呗,异步处理就要考虑线程安全和队列的问题 .

参考书籍 :

                  <<设计模式之禅 第二版>>
                  <<设计模式>>

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值