设计模式第八讲:观察者模式和中介者模式详解
一. 观察者模式
1. 背景
在现实世界中,许多对象并不是独立存在的,其中一个对象的行为发生改变可能会导致一个或者多个其他对象的行为也发生改变。例如,某种商品的物价上涨时会导致部分商家高兴,而消费者伤心;还有,当我们开车到交叉路口时,遇到红灯会停,遇到绿灯会行。这样的例子还有很多,例如,股票价格与股民、微信公众号与微信用户、气象局的天气预报与听众、小偷与警察等。
在软件世界也是这样,例如,Excel 中的数据与折线图、饼状图、柱状图之间的关系;MVC 模式中的模型与视图的关系;事件模型中的事件源与事件处理者。所有这些,如果用观察者模式来实现就非常方便。
2. 定义和特点
(1). 定义
指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。这种模式有时又称作发布-订阅模式、模型-视图模式,它是对象行为型模式。
PS: 观察者模式是 【1对多】的关系,后面的中介者模式是 【多对多】的关系。
(2). 优点:
A. 降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系。
B. 目标与观察者之间建立了一套触发机制。
(3). 缺点:
A. 目标与观察者之间的依赖关系并没有完全解除,而且有可能出现循环引用。
B. 当观察者对象很多时,通知的发布会花费很多时间,影响程序的效率。
3. 具体实现
(1). 模式结构
实现观察者模式时要注意具体目标对象和具体观察者对象之间不能直接调用,否则将使两者之间紧密耦合起来,这违反了面向对象的设计原则。
A. 抽象目标类:它提供了一个用于保存观察者对象的聚集类和增加、删除观察者对象的方法,以及通知所有观察者的抽象方法。
B. 具体目标类:也叫具体目标类,它实现抽象目标中的通知方法,当具体主题的内部状态发生改变时,通知所有注册过的观察者对象。
C. 抽象观察者:它是一个抽象类或接口,它包含了一个更新自己的抽象方法,当接到具体主题的更改通知时被调用。
D. 具体观察者:实现抽象观察者中定义的抽象方法,以便在得到目标的更改通知时更新自身的状态。
结构图如下:

(2). 使用场景
老师向学生发通知,老师是具体的目标类,学生是具体的观察者。
(3). 代码实操
抽象观察者和具体的观察者(学生类)
/// <summary>/// 抽象观察者/// </summary>public interface IObserver{/// <summary>/// 接收通知/// </summary>/// <param name="msg"></param>void ReceiveNotice(string msg);}/// <summary>/// 学生1/// (具体观察者)/// </summary>public class StudentObserver1 : IObserver{public void ReceiveNotice(string msg){Console.WriteLine($"学生1已经收到通知,内容为:{msg}");}}/// <summary>/// 学生2/// (具体观察者)/// </summary>public class StudentObserver2 : IObserver{public void ReceiveNotice(string msg){Console.WriteLine($"学生2已经收到通知,内容为:{msg}");}}
抽象目标类和具体目标类(老师类)
/// <summary>/// 抽象目标/// (定义操作观察者的相关方法)/// </summary>public abstract class AbstractSub{protected List<IObserver> obList = new List<IObserver>();/// <summary>/// 增加观察者/// </summary>/// <param name="ob"></param>public void AddOb(IObserver ob){if (!obList.Contains(ob)){obList.Add(ob);}}/// <summary>/// 删除观察者/// </summary>/// <param name="ob"></param>public void RemoveOb(IObserver ob){if (obList.Contains(ob)){obList.Remove(ob);} }/// <summary>/// 通知具体观察者的方法/// </summary>public abstract void notifyObserver(string msg);}/// <summary>/// 老师1/// 具体目标(发通知 发布者)/// </summary>public class TeacherSub1 : AbstractSub{/// <summary>/// 实现通知学生的业务/// </summary>public override void notifyObserver(string msg){foreach (var item in obList){//向每个学生(即观察者)发送通知//当观察者较多的时候,此处考虑使用线程池item.ReceiveNotice(msg);}}}
测试
{//1. 声明所有学生类(观察者)IObserver student1 = new StudentObserver1();IObserver student2 = new StudentObserver2();//2. 声明老师类(发布者)AbstractSub teacher1 = new TeacherSub1();teacher1.AddOb(student1);teacher1.AddOb(student2);//3.发送通知teacher1.notifyObserver("2020-08-13, 晚上8点全体开会哦");}
运行结果
4. 适用场景分析
(1). 对象间存在一对多关系,一个对象的状态发生改变会影响其他对象。
(2). 当一个抽象模型有两个方面,其中一个方面依赖于另一方面时,可将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。
更多C++后端开发技术点知识内容包括C/C++,Linux,Nginx,ZeroMQ,MySQL,Redis,MongoDB,ZK,流媒体,音视频开发,Linux内核,TCP/IP,协程,DPDK多个高级知识点。
【文章福利】另外还整理一些C++后台开发架构师 相关学习资料,面试题,教学视频,以及学习路线图,免费分享有需要的可以点击 C++后端学习资料 免费领取

二. 中介者模式
1. 背景
在现实生活中,常常会出现好多对象之间存在复杂的交互关系,这种交互关系常常是“网状结构”,它要求每个对象都必须知道它需要交互的对象。例如,每个人必须记住他(她)所有朋友的电话;而且,朋友中如果有人的电话修改了,他(她)必须告诉其他所有的朋友修改,这叫作“牵一发而动全身”,非常复杂。
如果把这种“网状结构”改为“星形结构”的话,将大大降低它们之间的“耦合性”,这时只要找一个“中介者”就可以了。如前面所说的“每个人必须记住所有朋友电话”的问题,只要在网上建立一个每个朋友都可以访问的“通信录”就解决了。这样的例子还有很多,例如,你刚刚参力口工作想租房,可以找“房屋中介”;或者,自己刚刚到一个陌生城市找工作,可以找“人才交流中心”帮忙。
在软件的开发过程中,这样的例子也很多,例如,在 MVC 框架中,控制器(C)就是模型(M)和视图(V)的中介者;还有大家常用的 QQ 聊天程序的“中介者”是 QQ 服务器。所有这些,都可以采用“中介者模式”来实现,它将大大降低对象之间的耦合性,提高系统的灵活性。
2. 定义和特点
(1). 定义
定义一个中介对象来封装一系列对象之间的交互,使原有对象之间的耦合松散,且可以独立地改变它们之间的交互。中介者模式又叫调停模式,它是迪米特法则的典型应用。
PS:中介者模式是 【多对多】的关系,上面的观察者模式是 【1对多】的关系。
(2). 优点
A. 降低了对象之间的耦合性,使得对象易于独立地被复用。
B. 将对象间的多对多关联转变为一对一的关联,提高系统的灵活性,使得系统易于维护和扩展。
(3). 缺点
同事类太多时,中介者的职责将很大,它会变得复杂而庞大,以至于系统难以维护
3. 具体实现
(1). 模式结构
A. 抽象中介者:它是中介者的接口,提供了同事对象注册与转发同事对象信息的抽象方法。
B. 具体中介者:实现中介者接口,定义一个 List 来管理同事对象,协调各个同事角色之间的交互关系,因此它依赖于同事角色。
C. 抽象同事类:定义同事类的接口,保存中介者对象,提供同事对象交互的抽象方法,实现所有相互影响的同事类的公共功能。
D. 具体同事类:是抽象同事类的实现者,当需要与其他同事对象交互时,由中介者对象负责后续的交互。
结构图如下:

(2). 使用场景
一个公司中,不同部门的员工不允许直接对接,这个时候需要一个中间人,帮着沟通联系,这个中间人就是中介者。
(3). 代码实操
抽象中介者和具体中介者
/// <summary>/// 抽象中介者/// </summary>public abstract class AbstractMediator{//添加同事public abstract void AddColleague(AbstractColleague colleague);//消息转发public abstract void TransMsg(AbstractColleague cl,string msg); //转发}/// <summary>/// 具体中介者/// (用于协调转发各部门同事的信息传输)/// </summary>public class Mediator1 : AbstractMediator{private List<AbstractColleague> cList = new List<AbstractColleague>();/// <summary>/// 添加同事/// </summary>/// <param name="colleague"></param>public override void AddColleague(AbstractColleague colleague){if (!cList.Contains(colleague)){cList.Add(colleague);}}/// <summary>/// 消息转发(1对1)/// </summary>/// <param name="cl"></param>/// <param name="msg"></param>public override void TransMsg(AbstractColleague cl, string msg){foreach (var item in cList){if (item.Equals(cl)){item.ReceiveMsg(msg);}}}}
抽象同事类和具体同事类
/// <summary>/// 抽象同事类/// </summary>public abstract class AbstractColleague{protected AbstractMediator _abstractMediator;public AbstractColleague(AbstractMediator abstractMediator){this._abstractMediator = abstractMediator;}/// <summary>/// 收消息/// </summary>/// <param name="msg"></param>public abstract void ReceiveMsg(string msg);/// <summary>/// 发消息(这里指1对1)/// </summary>/// <param name="msg"></param>public abstract void SendMsg(AbstractColleague c, string msg);}/// <summary>/// 同事1(位于业务部门)/// </summary>public class Colleague1 : AbstractColleague{public Colleague1(AbstractMediator meditor):base(meditor){}public override void ReceiveMsg(string msg){Console.WriteLine($"同事1收到新消息,内容为:{msg}");}public override void SendMsg(AbstractColleague c, string msg){//调用中介者进行消息转发_abstractMediator.TransMsg(c, msg);}}/// <summary>/// 同事2(位于技术部门)/// </summary>public class Colleague2 : AbstractColleague{public Colleague2(AbstractMediator meditor) : base(meditor){}public override void ReceiveMsg(string msg){Console.WriteLine($"同事2收到新消息,内容为:{msg}");}public override void SendMsg(AbstractColleague c, string msg){//调用中介者进行消息转发_abstractMediator.TransMsg(c, msg);}}/// <summary>/// 同事3,位于行政部门/// </summary>public class Colleague3 : AbstractColleague{public Colleague3(AbstractMediator meditor) : base(meditor){}public override void ReceiveMsg(string msg){Console.WriteLine($"同事3收到新消息,内容为:{msg}");}public override void SendMsg(AbstractColleague c, string msg){//调用中介者进行消息转发_abstractMediator.TransMsg(c, msg);}}
测试
{//1.声明中介者AbstractMediator aMediator = new Mediator1();//2. 声明各业务同事AbstractColleague c1 = new Colleague1(aMediator);AbstractColleague c2 = new Colleague2(aMediator);AbstractColleague c3 = new Colleague3(aMediator);//3.给中介者添加各个同事aMediator.AddColleague(c1);aMediator.AddColleague(c2);aMediator.AddColleague(c3);//4.开始信息交流Console.WriteLine("c1发消息给c2");c1.SendMsg(c2, "今晚一起吃饭吧");Console.WriteLine("c2发消息给c3");c3.SendMsg(c3, "明天8点一起开会");}
运行结果
4. 适用场景分析
(1). 当对象之间存在复杂的网状结构关系而导致依赖关系混乱且难以复用时。
(2). 当想创建一个运行于多个类之间的对象,又不想生成新的子类时。
原文链接:https://www.cnblogs.com/yaopengfei/p/13502916.html
相关文章:
设计模式第八讲:观察者模式和中介者模式详解
一. 观察者模式 1. 背景 在现实世界中,许多对象并不是独立存在的,其中一个对象的行为发生改变可能会导致一个或者多个其他对象的行为也发生改变。例如,某种商品的物价上涨时会导致部分商家高兴,而消费者伤心;还有&…...
关于 mac 本地配置域名能 ping 通,但是浏览器不能访问的问题(而其他电脑操作可访问)
关于 mac 本地配置域名能 ping 通,但是浏览器不能访问的问题(而其他电脑操作可访问)1. 配置域名的方式1.1 sudo vim /etc/hosts1.2 浏览器插件 LiveHosts2. 问题描述3. 解决问题方法3.1 尝试方法1—确保代理都关闭3.2 尝试方法2—确保域名能p…...
【代码随想录二刷】Day23-二叉树-C++
代码随想录二刷Day23 今日任务 669.修剪二叉搜索树 108.将有序数组转换为二叉搜索树 538.把二叉搜索树转换为累加树 语言:C 669. 修剪二叉搜索树 链接:https://leetcode.cn/problems/trim-a-binary-search-tree/ 递归 class Solution { public:Tree…...
Linux GPIO 开发指南
文章目录Linux GPIO 开发指南1 概述1.1 编写目的1.2 适用范围1.3 相关人员2 模块介绍2.1 模块功能介绍2.2 相关术语介绍2.3 总体框架2.4 state/pinmux/pinconfig2.5 源码结构介绍3 模块配置3.1 kernel menuconfig 配置3.2 device tree 源码结构和路径3.2.1 device tree 对 gpio…...
记一次后端生成Zip文件通过浏览器下载后文件损坏,无法打开,不可预知的末端错误,下载后文件比源文件增大
记一次后端生成Zip文件问题前言问题出现排查一、流没有关好二、写入了空白字节三、没有flush定位环节一、生成二、通过SwaggerUI、PostMan进行下载三、结论解决方法前言 在项目上线前夕,临时添加了个数据导出的接口,需求是导出压缩包,选择了项…...
python中savgol_filter的详细解释
目录savgol_filter简介savgol_filter原理参数window_length对平滑的效果参数polyorder的平滑效果savgol_filter简介 Savitzky-Golay滤波器最初由Savitzky和Golay于1964年提出,是光谱预处理中常用滤波方法,它的核心思想是对一定长度窗口内的数据点进行k阶…...
C语言--指针进阶1
目录回顾字符指针指针数组数组指针&数组名和数组名的区别数组指针的使用指针作为形参练习数组参数、指针参数一维数组传参二维数组传参一级指针传参二级指针传参回顾 指针的内容,我们在初级阶段已经有所涉及了,我们先来复习一下 指针就是个变量&am…...
ssh的使用
Halo,这里是Ppeua。平时主要更新C语言,C,数据结构算法......感兴趣就关注我吧!你定不会失望。 🌈个人主页:主页链接 🌈算法专栏:专栏链接 我会一直往里填充内容哒! &…...
Apache Hadoop生态-目录汇总-持续更新
目录 1:系统服务分布图 3台分布式架构 1台单机架构 服务版本介绍 2:服务目录 存储相关 数据采集 任务调度 即席查询 数据可视化 集群监控 元数据管理 用户认证 权限管理 第三方windows客户端 1:系统服务分布图 3台分布式架构…...
「JVM 编译后话」编译器优化技术
后端编译(即时编译、提前编译)的目标时将字节码翻译成本地机器码,而难点是输出优化质量较高的机器码; 文章目录1. 优化技术概览2. 方法内联(Inlining)3. 逃逸分析(Escape Analysis)4…...
【python学习笔记】:输出与输入
01 输出方式 表达式语句、print()函数和使用文件对象的write()方法。 02 输出形式 格式化输出str.format()函数、转成字符串可以使用repr()或str()函数来实现。 (1)repr():产生一个解释器易读的表达形式,便于字符串的拼接。 例:输出平方与…...
汽车电子社区交流宣传
http://t.csdn.cn/VSLO0http://t.csdn.cn/VSLO0 当今的汽车行业已经进入了数字化时代,汽车电子软件的开发变得越来越重要。在这个领域,开发者们需要应对各种挑战,包括复杂的硬件和软件交互、高效的嵌入式编程和安全性要求。为了帮助汽车电子…...
String、StringBuilder 和 StringBuffer 详解
碎碎念 这是一道老生常谈的问题了,字符串是不仅是 Java 中非常重要的一个对象,它在其他语言中也存在。比如 C、Visual Basic、C# 等。字符串使用 String 来表示,字符串一旦被创建出来就不会被修改,当你想修改StringBuffer 或者是 …...
windows服务器上传文件解决方案
1.说明 1.如果上传到linux系统,通常使用ftp相关技术,配合windows端的ftp客户端工具比如FileZilla等进行大文件的上传工作。 2.同理windows服务器也可以开启ftp服务用来传输大文件。 3.本文介绍偷懒方式(常规是开启windows的ftp服务࿰…...
Android Studio翻译插件推介(Translation)
前言 Android Studio翻译插件适合英语水平不太好的程序员(比如:我),最常用的翻译插件Translation和AndroidLocalize,本文主要讲解Translation,亲测可用。 先看看效果:这里是Android的API,任意选…...
DNS,DNS污染劫持,DNS加密
1. DNS(Domain Name System)DNS(Domain Name System), 也叫网域名称系统,是互联网的一项服务。它实质上是一个 域名 和 IP 相互映射的分布式数据库.DNS(Domain Name Server,域名服务…...
【Python】如何度量优秀代码——静态分析工具
静态分析工具背景有哪些静态分析工具呢度量Python代码的静态属性度量Python的生态系统代码的坏味道在类层面上在方法层面上结语背景 静态代码分析工具能够提炼出丰富的代码静态属性信息,这使得程序员可以对代码的复杂性、可修改性和可读性有进一步的了解。 有哪些…...
Open3D 点云高程归一化(基于2维地面点,Python版本)
文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 之前的博客中Open3D 点云高程归一化(基于地面点,Python版本)是基于三维空间进行最近地面点的查询操作,这里对其进行修改一下,将点云投影到水平面,基于二维空间进行最近地面点的查询,这种方式对一些较为陡峭的…...
动态系统的建模与分析
前言 CS小菜鸡控制理论入门 视频学习笔记 视频传送门:动态系统的建模与分析】9_一阶系统的频率响应_低通滤波器_Matlab/Simulink分析 拉普拉斯变换 F(s)L{f(t)}∫0∞f(t)e−stdtF(s)\mathcal{L}\{f(t)\}\int_0^\infty f(t)e^{-st}\mathrm{d}tF(s)L{f(t)}∫0∞f(t)…...
QCC51XX---HCI log
高通在新的S3/S5以及往后新的平台上面,引入了一个新的调试功能。就是标题说的HCI log,他类似air trace那样用来分析蓝牙协议的,这样我们就可以很详细地找到通信协议之间哪个部分出了问题。以前我们都是通过抓包器抓air trace分析的,抓包器一个要几十万,学会这个功能就相当…...
SDMatte算法原理浅析:从卷积神经网络看图像分割技术
SDMatte算法原理浅析:从卷积神经网络看图像分割技术 1. 效果展示:当AI学会"精准抠图" 先来看一组实际案例。左边是原始图片,右边是SDMatte算法的处理结果: 你会注意到,即便是复杂场景下的发丝、半透明物体…...
GLM-OCR系统资源优化:C盘清理与显存高效利用技巧
GLM-OCR系统资源优化:C盘清理与显存高效利用技巧 你是不是也遇到过这种情况:兴致勃勃地部署好GLM-OCR,准备大展身手,结果没跑几天,系统就弹窗提示“C盘空间不足”,或者程序运行越来越慢,甚至直…...
LVGL8实战:打造个性化数字密码键盘界面
1. 为什么需要自定义密码键盘 在智能家居控制面板、金融支付终端这类对安全性要求较高的场景中,系统自带的软键盘往往存在两个致命问题:一是界面风格与产品整体设计语言不协调,二是可能存在输入轨迹泄露的风险。去年我给某智能门锁厂商做方案…...
Llama-3.2V-11B-cot保姆级教学:Streamlit缓存机制加速推理响应
Llama-3.2V-11B-cot保姆级教学:Streamlit缓存机制加速推理响应 1. 项目概述 Llama-3.2V-11B-cot是基于Meta Llama-3.2V-11B-cot多模态大模型开发的高性能视觉推理工具,专为双卡4090环境深度优化。这个工具解决了视觉权重加载的关键Bug,支持…...
告别官方版SSE2坑!用linsys_pjsip 2.11.8在ARM32平台快速集成SIP与WebRTC AEC3
ARM32平台高效集成SIP与WebRTC AEC3:linsys_pjsip 2.11.8实战指南 在嵌入式音视频通信领域,ARM32架构设备因其低功耗和成本优势被广泛应用。但当你尝试在这些设备上部署PJSIP时,官方版本的SSE2指令集依赖就像一堵高墙,让许多开发者…...
Unity 2018 + Facebook SDK 7.15.1避坑指南:从崩溃解决到完整功能实现
Unity 2018与Facebook SDK 7.15.1深度适配实战手册 当老牌游戏引擎遇上社交巨头的SDK,版本兼容性问题往往成为开发者的噩梦。本文将带您深入探索Unity 2018与Facebook SDK 7.15.1这对"经典组合"的适配之道,从环境搭建到功能实现,完…...
Activiti7实战指南:从流程实例到任务分配的全流程解析
1. Activiti7流程引擎核心概念解析 Activiti7作为当前最流行的开源工作流引擎之一,在企业级业务流程管理中扮演着重要角色。我第一次接触Activiti是在2014年参与某金融项目的审批系统开发时,当时就被它优雅的设计理念所吸引。经过多年实战,我…...
别再说AI懂你了!先搞清楚AI中的Context到底是什么(上篇)
你有没有遇到过这种情况——跟ChatGPT聊了五句话,第四句你说了“那个方案不行”,第五句它问“哪个方案?”。或者你让AI写一篇关于“苹果”的文章,它给你写了一整页水果种植技术,而你想说的是苹果公司。这就是AI中的Con…...
光刻胶选型避坑指南:从正胶负胶到配套试剂的全流程解析
光刻胶选型避坑指南:从正胶负胶到配套试剂的全流程解析 在半导体制造领域,光刻工艺的质量直接决定了芯片的性能和良率。而光刻胶作为光刻工艺的核心材料,其选型往往成为工艺工程师最头疼的问题之一。我曾亲眼见过一个团队因为选错光刻胶类型&…...
深入解析epoll:高并发网络编程核心技术
1. 理解高并发场景下的网络通信挑战在现代网络服务中,处理大量并发连接是一个常见需求。想象一个即时通讯服务器需要同时维持上百万用户的TCP连接,但实际活跃用户(正在收发消息的)可能只有几百个。传统做法如select/poll需要每次将…...
