观察者模式的应用之模拟事件监听系统

本文深入探讨了观察者模式的本质及应用场景,包括模式的基本原理、触发联动机制、目标对象与观察者的关系,以及如何实现不同类型的观察者响应。此外,还讨论了推模型与拉模型的区别,并通过一个水质监测系统的实例展示了如何根据不同的条件通知特定的观察者。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

思考观察者模式

(1)观察者模式的本质触发联动。当修改目标对象的状态时,就会触发相应的通知,然后会循环调用所有观察者对象的相应方法,通知这些观察者,让其做相应的反应。其实就相当于联动调用这些观察者的方法。它将目标对象与观察者解耦,这样目标对象与观察者可以独立变化,但又可以正确联动起来。

(2)目标对象和观察者之间的关系

  ①一个目标只有一个观察者,也可以被多个观察者观察。

  ②一个观察者可以观察多个目标对象,但这里一般观察者内要提供不同的update方法,以便让不同的目标对象来回调。

  ③在观察者模式中,观察者和目标是单向依赖的,只有观察者依赖于目标,而目标是不依赖于具体的观察者(只依赖于接口)。

  ④它们之间的联系主动权掌握在目标对象手中,只有目标对象知道什么时候需要通知观察者。在整个过程中,观察者始终是被动地等待目标对象的通知

  ⑤对于目标对象而言,所有的观察者都是一样的,会一视同仁。但也可以在目标对象里面进行控制,实现有区别的对待观察者

(3)基本的实现说明

  ①具体的目标实现对象要能维护观察者的注册信息,最简单的实现方案就是采用链表来保存观察者的注册信息。

  ②具体的目标实现对象需要维护引起通知的状态,一般情况下是目标自身的状态。变形使用的情况下,也可以是别的对象的状态。

  ③具体的观察者实现对象需要能接收目标的通知,能够接收目标传递的数据或主动去获取目标的数据,并进行后续处理。

  ④如果是一个观察者观察多个目标,那在观察者的更新方法里面,需要去判断是来自哪一个目标的通知。一种简音的解决方案是扩展update方法,比哪里在方法里多传递一个参数进行区分。还有一种更简单的方法,就是定义不同的回调方法。

(4)触发通知的时机

  ①一般是在完成状态维护后触发,因为通知会传递数据,不能够先通知后改数据,这很容易导致观察者和目标对象的状态不一致。

  ②可能出错的示例代码片段

(5)相互观察

  在某些应用中,可能会出现目标和观察者相互观察的情况。这种情况要防止可能出现的死循环现象。

推模型和拉模型

(1)推模型目标对象主动向观察者推送目标的详细信息,不管观察者是否需要,推送的消息通常是目标对象的全部或部分数据。相当于是在广播通知

(2)拉模型目标对象在通知观察者的时候,只传递少量信息。如果观察者需要更具体的信息,由观察者主动到目标对象中获取,相当于是观察者从目标对象中拉数据。一般这种模型的实现中,会把目标对象自己通过update方法传递给观察者,这样在观察者需要获取数据的时候,就可以通过这个引用来获取。

(3)关于两种模型的比较

  ①推模型是假定目标对象知道观察者需要的数据;而拉模型是目标对象不知道观察者具体需要什么数据,在没有办法的情况下,干脆把自身传递给观察者,让观察者自己去按需取值。

  ②推模式可能会使观察者对象难以复用,因为观察者定义的update方法是按需定义的,可能无法兼顾没有考虑到的情况。这意味者出现新的情桨叶时,就可以需要提供新的update方法。或干脆重新实现观察者。而拉模式不会造成这种情况,因为update方法的参数是目标对象本身。这基本上是目标对象能传递的最大数据集合,基本可以适应各种情况的需要。

观察者模式的优缺点

(1)优点

  ①观察者模式实现了观察者和目标之间的抽象耦合

  ②实现了动态联动。由于观察者模式对观察者的注册实行管理,那就可以在运行期间,通过动态地控制注册的观察者,来控制某个动作的联动范围,从而实现动态联动。

  ③支持广播通信

(2)缺点

  可能会引起无谓的操作。由于观察者模式每次都是广播通信,不管观察者需不需要,每个观察者都会被调用update方法。如果观察者不需要执行相应处理,那这次操作就浪费了,甚至可能会误操作。如本应在执行这次状态更新前把某个观察者删除掉,但现在这个观察者都还没删除,消息就又到达了,那么就会引起误操作。

观察者模式的应用场景

(1)聊天室程序,服务器转发给所有客户端,群发消息等

(2)网络游戏(多人联机对战)场景中,服务器将客户端的状态进行分发。

(3)事件处理模型,基于观察者模式的委派事件模型(事件源:目标对象;事件监听器:观察者)

【编程实验】区别对待观察者(变式观察者模式)

//声明文件

//*****************************************************************************************
//行为型模式——观察者模式
//场景:水质监测系统(拉模型)
/*、
说明:
    1、水质正常时:只通知监测人员做记录
    2、轻度污染时:除了通知监测人员做记录外,还要通知预警人员,判断是否需要预警
    3、中度或重度污染时:除了通知以上两人种外,还要通知部门领导做相应的处理
解决方式:
    1、每次污染时,目标可以通知所有观察者,由观察者决定是否属自己处理的情况
    2、每次污染时,在目标里进行判断,然后只通知相应的观察者(本例采用这种方式)
*/

#include <iostream>
#include <string>
#include <list>

using namespace std;


class CWatcherObserver;//前向声明

//定义水质监测的目标对象
class CWaterQualitySubject{
protected:
	list<CWatcherObserver*> lstObs;
public:
	void Attach(CWatcherObserver* observer);
	void Detach(CWatcherObserver* observer);
	 //通知相应的观察者对象
	virtual void Notify() = 0;
	 //获取水质污染的级别
	virtual void SetPolluteLevel(int level) = 0;
	virtual int GetPolluteLevel() = 0;
};
//水质观察者接口定义
class CWatcherObserver{
public:
	//被通知时的处理方法,参数为被观察的目标对象
	virtual void Update(CWaterQualitySubject* subject) = 0;
	 //设置和获取观察人员的职务
	virtual void SetJob(string job) = 0;
	virtual string GetJob() = 0;
};

//具体的观察者
class CWatcher : public CWatcherObserver{
private:
	string strJob;
public:
	void SetJob(string job);
	string GetJob();
	void Update(CWaterQualitySubject* subject);
};
//具体的水质监测对象
class CWaterQuality : public CWaterQualitySubject{
private:
	int iPolluteLevel; //0正常,1轻度污染,2中度污染,3重度污染
public:
	CWaterQuality();
	 //通知相应的观察者
	void SetLevel(int level);
	void SetPolluteLevel(int level);
	int GetPolluteLevel();
	void Notify();
};

//实现文件

//*******************************************************************************************
void CWaterQualitySubject::Attach(CWatcherObserver* observer){lstObs.push_back(observer);}
void CWaterQualitySubject::Detach(CWatcherObserver* observer){lstObs.remove(observer);}

//具体的观察者
void CWatcher::SetJob(string job){strJob = job;}
string CWatcher::GetJob(){return strJob;}
void CWatcher::Update(CWaterQualitySubject* subject){ //收到通知时的处理过程
	cout << strJob << "获取到通知,当前污染级别为:" << subject->GetPolluteLevel() << endl;
}
//具体的水质监测对象
CWaterQuality::CWaterQuality(){iPolluteLevel = 0;}
//通知相应的观察者
void CWaterQuality::SetLevel(int level){
	iPolluteLevel = level;
	Notify();
}
void CWaterQuality::SetPolluteLevel(int level){iPolluteLevel = level;}
int CWaterQuality::GetPolluteLevel(){return iPolluteLevel;}
void CWaterQuality::Notify(){
	for(list<CWatcherObserver*>::iterator it = lstObs.begin(); it != lstObs.end(); it++){
		//根据污染级别判断是否需要通知

		//通知监测员记录
		if(iPolluteLevel >= 0){
			if((*it)->GetJob() == "监测人员")				(*it)->Update(this);
		}
		else if(iPolluteLevel >= 1){
			if((*it)->GetJob() == "预警人员")				(*it)->Update(this);
		}
		else if(iPolluteLevel >= 2){
			if((*it)->GetJob() == "监测部门领导")		(*it)->Update(this);			
		}
	}
}

//测试客户端

void main()
{
	//创建水质主题对象
	CWaterQuality oWaterQuality;
	//创建几个观察者
	CWatcherObserver* pWatcher1 = new CWatcher(); pWatcher1->SetJob("监测人员");
	CWatcherObserver* pWatcher2 = new CWatcher(); pWatcher2->SetJob("预警人员");
	CWatcherObserver* pWatcher3 = new CWatcher(); pWatcher3->SetJob("监测部门领导");
	 //注册观察者
	oWaterQuality.Attach(pWatcher1);oWaterQuality.Attach(pWatcher2);oWaterQuality.Attach(pWatcher3);

	//填写水质报告
	cout << endl << "当水质正常的时候---------------------------" << endl;
	oWaterQuality.SetLevel(0);
	cout << endl << "当水质轻度污染的时候--------------------" << endl;
	oWaterQuality.SetLevel(0);
	cout << endl << "当水质中度污染的时候--------------------" << endl;
	oWaterQuality.SetLevel(0);

	delete pWatcher1; delete pWatcher2; delete pWatcher3;
	
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值