设计模式第八讲:观察者模式和中介者模式详解
一. 观察者模式
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分析的,抓包器一个要几十万,学会这个功能就相当…...
AI Agent在智能风控中的实战:多智能体欺诈检测与预警
AI Agent在智能风控中的实战:多智能体欺诈检测与预警 你有没有过明明是正常交易却被银行冻结账户的糟糕体验?或是听说过某电商平台上线新活动首日就被黑产团伙薅走数千万补贴的新闻?随着黑产欺诈向团伙化、专业化、动态化演进,传统依赖规则引擎、单模型机器学习的风控体系已…...
Allegro等长设置翻车实录:拓扑模板法的3个坑与手工PinPair的救赎
Allegro等长设计避坑指南:从拓扑模板到精准PinPair的实战演进在高速PCB设计中,等长匹配如同精密钟表里的齿轮啮合,差之毫厘便可能导致整个系统时序崩塌。当设计从简单的点对点结构升级到多负载复杂拓扑时,Allegro用户常陷入两种典…...
Java数组工具类实战:设计不可实例化的静态工具类
实现一个工具类 MathUtils,满足以下要求: 1. 所有方法均为静态,且该类不能从外部实例化(提示:使用私有构造器)。 2. 提供三个静态方法:- maxArray(int[] arr):返回较大值;…...
Agent开发面试通关攻略:吃透稳拿offer
阅读前置:2026年当下最卷也最缺人的AI岗位,一定是AI Agent开发。最近刷遍CSDN、牛客、力扣最新面经,发现一个非常明显的招聘趋势:普通大模型微调岗位饱和内卷,而AI Agent开发岗位人才严重缺口,薪资更高、竞…...
Veo 2胶片质感生成器失效?——深度解析Color Science v2.3内核中被屏蔽的Cinematic Grain Injection层
更多请点击: https://kaifayun.com 第一章:Veo 2胶片质感生成器失效现象全景透视 近期大量用户反馈,Veo 2 胶片质感生成器在调用 generate_film_effect() 接口后返回空纹理、纯灰帧或 HTTP 503 Service Unavailable 错误,且该问题…...
Hindsight测试策略:单元测试、集成测试和端到端测试
Hindsight测试策略:单元测试、集成测试和端到端测试 【免费下载链接】hindsight Hindsight: Agent Memory That Learns 项目地址: https://gitcode.com/GitHub_Trending/hindsight2/hindsight Hindsight作为一款专注于Agent Memory的开源项目,其可…...
武汉国电华美串联谐振试验装置,现场用着心里有底
在高压试验现场干了这么多年,这位老师傅常说,一台好的串联谐振装置,就是试验人员的胆。面对GIS、大型变压器、超高压电缆这些大电容试品,没有趁手的谐振设备,交流耐压试验根本没法干。16875kVA/225kV这个规格ÿ…...
告别DLL缺失烦恼!Visual C++运行库合集一键搞定Windows应用依赖问题
告别DLL缺失烦恼!Visual C运行库合集一键搞定Windows应用依赖问题 【免费下载链接】vcredist AIO Repack for latest Microsoft Visual C Redistributable Runtimes 项目地址: https://gitcode.com/gh_mirrors/vc/vcredist 你是否曾经在打开某个软件或游戏时…...
终极指南:用D2DX让《暗黑破坏神2》在现代电脑上焕发新生
终极指南:用D2DX让《暗黑破坏神2》在现代电脑上焕发新生 【免费下载链接】d2dx D2DX is a complete solution to make Diablo II run well on modern PCs, with high fps and better resolutions. 项目地址: https://gitcode.com/gh_mirrors/d2/d2dx 还在为经…...
告别漫长等待:UE5.2.1 Windows打包效率优化与插件问题排查指南
告别漫长等待:UE5.2.1 Windows打包效率优化与插件问题排查指南第一次点击"打包项目"按钮时,进度条仿佛被冻结的场景,每个UE5开发者都经历过。尤其当项目规模达到数十GB时,等待时间可能超过一小时——这背后隐藏着引擎底…...
