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,3dean.SelectCourse(stu1, 1);dean.SelectCourse(stu1, 2);dean.SelectCourse(stu1, 3);//李四选了课程2,3dean.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,3dean.SelectCourse(stu1, 1);dean.SelectCourse(stu1, 2);dean.SelectCourse(stu1, 3);//李四选了课程2,3dean.SelectCourse(stu2, 2);dean.SelectCourse(stu2, 3);//教务处发通知让学生上课int classId;while (1){cout << "请输入课程ID:>>";cin >> classId;if (classId <= 0)break;dean.NoticeStudent(classId);}
}

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

  1. 2.设计模式-观察者模式(发布-订阅模式)

    观察者模式(发布-订阅模式)一个简单的使用 简介 例子 被监听者(被观察者) 监听者(观察者) 测试类 简介 观察者模式(有时又被称为模型(Model)-视图(View)模式.源-收听者(Listen ...

  2. python的编程模式-Python设计模式之状态模式原理与用法详解

    本文实例讲述了Python设计模式之状态模式原理与用法.分享给大家供大家参考,具体如下: 状态模式(State Pattern):当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类 ...

  3. 设计模式 - 抽象工厂模式(abstract factory pattern) 详解

    抽象工厂模式(abstract factory pattern) 详解 本文地址: http://blog.csdn.net/caroline_wendy/article/details/270916 ...

  4. async spring 默认线程池_Spring boot注解@Async线程池实例详解

    这篇文章主要介绍了Spring boot注解@Async线程池实例详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 从Spring3开始提供了@A ...

  5. 线程池源代码详解,参数详解

    线程池源代码详解,参数详解 ThreadPoolExecutor 构造函数源代码 public ThreadPoolExecutor(int corePoolSize, int maximumPool ...

  6. Java线程池ThreadPool详解

    Java线程池ThreadPool详解 1. 线程池概述 1.1 线程池简介 1.2 线程池特点 1.3 线程池解决问题 2. 线程池原理分析 2.1 线程池总体设计 2.6 线程池流转状态 2.2 ...

  7. 深入剖析Redis系列(三) - Redis集群模式搭建与原理详解

    前言 在 Redis 3.0 之前,使用 哨兵(sentinel)机制来监控各个节点之间的状态.Redis Cluster 是 Redis 的 分布式解决方案,在 3.0 版本正式推出,有效地解决了 ...

  8. 怎样进入android模式,安卓手机如何进入Recovery模式的通用方式详解

    2014-12-12 15:24:16 安卓手机如何进入Recovery模式的通用方式详解 标签:安卓 Recovery模式 教程 Recovery模式是什么?这里说的Recovery模式主要指的是安 ...

  9. android doze模式源码分析,Android Doze模式启用和恢复详解

    从Android 6.0(API level 23)开始,Android提出了两个延长电池使用时间的省电特性给用户.用户管理可以在没有充电的情况下管理app的行为.当用户一段时间没有使用手机的时候,D ...

最新文章

  1. 禅道设置bug模板_JPress v3.0 beta.2 发布,修复 bug 和完善产品细节
  2. or函数python_Python numpy.bitwise_or函数方法的使用
  3. 由“ASP.NET网站限制访问频率”想到的两点问题(转)
  4. 【STM32】FreeRTOS创建和删除任务示例(静态方法)(了解)
  5. python之禅中文原文_Python之禅 - osc_ns45oss7的个人空间 - OSCHINA - 中文开源技术交流社区...
  6. Solr部分更新MultiValued的Date日期字段时报错及解决方案:Invalid Date String:‘Mon Sep 14 01:48:38 CST 2015‘
  7. POJ NOI0105-45 金币
  8. 适用于stuido one的虚拟贝斯手插件:UJAM Virtual Bassist ROYAL for Mac
  9. CSDN九年的博客时光
  10. 二次拟合r方_excel曲线拟合中的决定系数R平方是如何求出来的?
  11. 数字图像处理 冈萨雷斯 中文第三版 习题
  12. java game nokia 5233 model,诺基亚S60/^3完美运行GBA游戏教程 重回孩提时代
  13. 搭建 Python 开发环境
  14. Chrome如何安装第三方扩展插件(crx)
  15. 小米盒子 改装 无线打印服务器,终于找到了“小米盒子增强版”不定期断网的bug了,2.4G模块bug!...
  16. 学生评语管理系统软件测试,学生评语管理系统测试版
  17. 快乐共享(By Robinvane Suen)
  18. 原来小米手机这么好用,这4大功能,各个都是黑科技,厉害了
  19. 包裹侠快递查询_全球顶尖技术精英汇聚菜鸟 准备帮助快递攻破体积测量难题...
  20. MySQL 正负数排序

热门文章

  1. 小程序weui组件使用
  2. 夏天吃海鲜的八大禁忌
  3. 备战面试日记(3.3) - (设计模式.23种设计模式之结构型模式)
  4. Proxmox VE7.3+Ceph超融合私有云建设案例(低成本高价值,拿走不谢)
  5. 好物推荐:2020 年常用软件锦集!帮你解决 90% 的软件问题
  6. C语言二级必须编译吗,c语言是计算机二级吗
  7. IDEA导入scala详解
  8. 理解假设检验: 统计学意义上的显著性水平 (Alpha) 和P值
  9. MATLAB仪表表盘数字识别
  10. 知网caj文件转pdf(无需下载任何软件,傻瓜式操作,必看!!!)