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);
}
}