行为者模式1——观察者模式(发布-订阅)

观察者模式

该模式也叫作发布-订阅模式,观察者模式主要关注的是对象的一对多的关系,也就是多个对象依赖于一个对象,当该对象的状态发生改变时,其他对象都能够收到相应的通知并自动更新

我们可以将观察者模式理解为,订阅一份报纸。首先决定订阅报纸的时候,需要去报刊告诉相关工作人员;填写自己订阅的信息,比如想要订阅什么类型的报纸(体育类、财经类等),之后留下自己的住址(这样就可以享受送报上门服务了)。

示例:
一组数据(数据对象)—— 曲线图(对象1)/ 柱状图(对象2)/ 圆饼图(对象3)

当这组数据发生变化时,对象123应该及时收到相应的通知。

我们依然使用一个情境对该模式进行学习:

  • 简单的设置三个观察者,一个主题类(被观察者)。
  • 这三个观察者可以设置自己喜欢的、感兴趣的消息类型(1、2、3)。
  • 他们处理收到的消息就是打印一下自己收到了什么消息。
//观察者的抽象类
class Observer
{
public:
	// 处理消息的接口
	virtual void handle(int msgid) = 0;
};
// 第一个观察者实例,主要对12消息感兴趣
class Observer1 :public Observer
{
public:
	void handle(int msgid)
	{
		switch (msgid)
		{
		case 1:
			cout << "Observer1 recv 1 msg!" << endl;
			break;
		case 2:
			cout << "Observer1 recv 2 msg!" << endl;
			break;
		default:
			cout << "Observer1 recv unkown msg!" << endl;
			break;
		}
	}
};
// 第二个观察者实例,主要对2消息感兴趣
class Observer2 :public Observer
{
public:
	void handle(int msgid)
	{
		switch (msgid)
		{
		case 2:
			cout << "Observer2 recv 2 msg!" << endl;
			break;
		default:
			cout << "Observer2 recv unkown msg!" << endl;
			break;
		}
	}
};
// 第三个观察者实例,主要对13消息感兴趣
class Observer3 :public Observer
{
public:
	void handle(int msgid)
	{
		switch (msgid)
		{
		case 1:
			cout << "Observer3 recv 1 msg!" << endl;
			break;
		case 3:
			cout << "Observer3 recv 3 msg!" << endl;
			break;
		default:
			cout << "Observer3 recv unkown msg!" << endl;
			break;
		}
	}
};

// 主题类:需要存储每一个观察者感兴趣的事件有哪些
class Subject
{
public:
	// 给主题对象增加观察者对象
	void addObserver(Observer* obser, int msgid)
	{
		_subMap[msgid].push_back(obser);
	}
	// 主题通知:主题发生改变,通知相应的观察者处理事件
	void dispatch(int msgid)
	{
		auto it = _subMap.find(msgid);
		if (it != _subMap.end())
		{
			for (Observer* obs : it->second)
			{
				obs->handle(msgid);
			}
		}
	}
private:
	unordered_map<int, list<Observer*>> _subMap;
};

一些问题的解读:

  1. 我们可以看到主题类(Subject)的数据成员是一个unordered_map。使用这个是因为我们不需要数据是有序的,为了提高增删查的速率,使用了无序map。
  2. 使用map的好处是,它作为一个键值对,可以存储我们想要的数据类型:(消息类型,订阅此消息类型的观察者们)
  3. 因为同样的消息类型,可能有多个观察者,所以,unordered_map的第二个参数我们使用了list,来存储订阅此消息类型的所有观察者。
  4. 并且,在主题类(Subject)的成员方法addObserver中,我们使用了一个中括号运算符([])重载的特性: 如果当前容器中存有相应的msgid键的话,就直接添加对应的值(Obser);如果当前容器中没有相应的msgid键的话,就直接添加该键,并且添加一个默认的值。

测试部分:
将每个观察者(p1、p3、p3)所感兴趣的消息,添加到主题类(Subject)中;
之后用户自己输入状态改变信息(mgsid),这个时候主题类Subject通过dispatch方法,通知给所有的观察者

int main()
{
	Subject sub;
	Observer* p1 = new Observer1();
	Observer* p2 = new Observer2();
	Observer* p3 = new Observer3();

	sub.addObserver(p1, 1);
	sub.addObserver(p1, 2);
	sub.addObserver(p2, 2);
	sub.addObserver(p3, 1);
	sub.addObserver(p3, 3);

	int msgid = 0;
	for (;;)
	{
		cout << "请输入消息id:";
		cin >> msgid;
		if (msgid == -1)
			break;
		sub.dispatch(msgid);
	}

	return 0;
}

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值