设计模式——观察者(监听者,订阅)模式||线程安全||c++详解

1.什么是观察者模式

观察者模式常用于解耦事件的观察和最终的处理方式。它是一种对象行为模式,如果对象间存在着一种一对多的依赖关系,当一个对象发生改变的时候,其他依赖此对象的对象都要做出相应的改变。

举个例子:大家现在对公众号都不陌生,也经常会关注公众号。那么我们关注公众号的这种模式其实就是一种观察模式,当我们关注的公众号有新内容发布的时候,就会推送给我们这些关注了此公众号的人,那么那么没有关注此公众号的人就不会收到此公众号发布的内容。那么此时的公众号就相当于一个观察者,我们就相当于聆听者。观察者发现有内容推送,就推送给已经关注了它的人,我们呢收到内容,进行阅读(行为改变)。

模式UML图:

如上介绍的例子,订阅公众号的人就相当于Listen,公众号就相当于Observer.

还有一个就是我们现在高等院校选课的一个情景,每个学生都相当于一个聆听者,教务处的系统呢就相当于一个观察者,我们在教务系统选完课以后,就等待教务处通知我们进行上课,每个人都会收到自己所选课程的上课时间表,当教务处发出要上什么课的通知的时候,会将上课时间发送给那些选了这门课程的同学,同学们收到通知去出席课堂。

我们已选课的模式为例实现观察者模式。

2.观察者模式

# include<iostream>
using namespace std;
# include<unordered_map>
//学生基类
class Student
{
public:
	Student(string name):_name(name) {}
	//学生上课事件
	virtual void AttendClass(int classId) = 0;
protected:
	string _name;
};
//两个学生
class Student1 :public Student
{
public:
	Student1(string name):Student(name){}
	void AttendClass(int classId)
	{
		cout << "学生" << _name << "在上课程" << classId << endl;
	}
};
class Student2 :public Student
{
public:
	Student2(string name) :Student(name) {}
	void AttendClass(int classId)
	{
		cout << "学生" << _name << "在上课程" << classId << endl;
	}
};
//教务系统
class DeanSys
{
public:
	//学生选课
	void SelectCourse(Student* stu, int classId)
	{
		//判断当前课程是第一次被人选还是已经有人选过了
		unordered_map<int, list<Student*>>::iterator it = _course.find(classId);
		if (it == _course.end())
		{
			_course[classId].push_back(stu);
		}
		else
		{
			it->second.push_back(stu);
		}
	}

	//通知学生上课
	void NoticeStudent(int classId)
	{
		unordered_map<int, list<Student*>>::iterator it = _course.find(classId);
		if (it != _course.end())
		{
			for (Student* stu : it->second)
			{
				stu->AttendClass(classId);
			}
		}
	}

private:
	//记录学生和课程信息
	unordered_map<int, list<Student*>> _course;

};
int main()
{
	//实例两个学生
	Student* stu1 = new Student1("张三");
	Student* stu2 = new Student2("李四");
	//教务系统
	DeanSys dean;
	//张三选了课程1,2,3
	dean.SelectCourse(stu1, 1);
	dean.SelectCourse(stu1, 2);
	dean.SelectCourse(stu1, 3);
	//李四选了课程2,3
	dean.SelectCourse(stu2, 2);
	dean.SelectCourse(stu2, 3);
	//教务处发通知让学生上课
	int classId;
	while (1)
	{
		cout << "请输入课程ID:>>";
		cin >> classId;
		if (classId <= 0)
			break;
		dean.NoticeStudent(classId);
	}
        delete stu1;
        delete stu2;
}

程序运行结果:

3.线程安全的观察者模式

上述代码中,教务处(观察者)通知学生(聆听者)上课的方法下所示:

//通知学生上课
	void NoticeStudent(int classId)
	{
		unordered_map<int, list<Student*>>::iterator it = _course.find(classId);
		if (it != _course.end())
		{
			for (Student* stu : it->second)
			{
				stu->AttendClass(classId);
			}
		}
	}

那么在多线程种调用stu->AttendClass(classId)的时候,观察者没有办法知道聆听者对象是否还存在。这时,如果聆听者对象已经不存在,那么这句代码就会产生错误。这就是多线程下共享对象的安全问题。

我们知道C++11提供了的强弱智能指针能很好的解决这一个问题,关于智能指针的介绍参考我的另一篇文章:

https://blog.csdn.net/qq_42214953/article/details/88936479

接下来就用C++智能指针来实现线程安全的观察者模式:

# include<iostream>
using namespace std;
# include<unordered_map>
class Student
{
public:
	Student(string name):_name(name) {}
	//学生上课事件
	virtual void AttendClass(int classId) = 0;
protected:
	string _name;
};
class Student1 :public Student
{
public:
	Student1(string name):Student(name){}
	void AttendClass(int classId)
	{
		cout << "学生" << _name << "在上课程" << classId << endl;
	}
};
class Student2 :public Student
{
public:
	Student2(string name) :Student(name) {}
	void AttendClass(int classId)
	{
		cout << "学生" << _name << "在上课程" << classId << endl;
	}
};

//教务系统
class DeanSys
{
public:
	//学生选课
	void SelectCourse(weak_ptr<Student> stu, int classId)
	{
		//判断当前课程是第一次被人选还是已经有人选过了
		unordered_map<int, list<weak_ptr<Student>>>::iterator it = _course.find(classId);
		if (it == _course.end())
		{
			_course[classId].push_back(stu);
		}
		else
		{
			it->second.push_back(stu);
		}
	}

	//通知学生上课
	void NoticeStudent(int classId)
	{
		unordered_map<int, list<weak_ptr<Student>>>::iterator it = _course.find(classId);
		if (it != _course.end())
		{
		
			for (list<weak_ptr<Student>>::iterator stu = it->second.begin(); stu != it->second.end(); ++stu)
			{
                                //弱智能指针提升为强智能指针来进行访问操作
				shared_ptr<Student> p = stu->lock();
				if (p != nullptr)
				{
					p->AttendClass(classId);
				}
				else
				{
					stu = it->second.erase(stu);
				}
			}
		}
	}

private:
	//记录学生和课程信息
	unordered_map<int, list<weak_ptr<Student>>> _course;

};
int main()
{
	//实例两个学生
	shared_ptr<Student> stu1( new Student1("张三"));
	shared_ptr<Student> stu2 ( new Student2("李四"));
	//教务系统
	DeanSys dean;
	//张三选了课程1,2,3
	dean.SelectCourse(stu1, 1);
	dean.SelectCourse(stu1, 2);
	dean.SelectCourse(stu1, 3);
	//李四选了课程2,3
	dean.SelectCourse(stu2, 2);
	dean.SelectCourse(stu2, 3);
	//教务处发通知让学生上课
	int classId;
	while (1)
	{
		cout << "请输入课程ID:>>";
		cin >> classId;
		if (classId <= 0)
			break;
		dean.NoticeStudent(classId);
	}
}

 

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值