当前位置: 首页 > news >正文

设计模式第八讲:观察者模式和中介者模式详解

一. 观察者模式

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. 背景 在现实世界中&#xff0c;许多对象并不是独立存在的&#xff0c;其中一个对象的行为发生改变可能会导致一个或者多个其他对象的行为也发生改变。例如&#xff0c;某种商品的物价上涨时会导致部分商家高兴&#xff0c;而消费者伤心&#xff1b;还有&…...

关于 mac 本地配置域名能 ping 通,但是浏览器不能访问的问题(而其他电脑操作可访问)

关于 mac 本地配置域名能 ping 通&#xff0c;但是浏览器不能访问的问题&#xff08;而其他电脑操作可访问&#xff09;1. 配置域名的方式1.1 sudo vim /etc/hosts1.2 浏览器插件 LiveHosts2. 问题描述3. 解决问题方法3.1 尝试方法1—确保代理都关闭3.2 尝试方法2—确保域名能p…...

【代码随想录二刷】Day23-二叉树-C++

代码随想录二刷Day23 今日任务 669.修剪二叉搜索树 108.将有序数组转换为二叉搜索树 538.把二叉搜索树转换为累加树 语言&#xff1a;C 669. 修剪二叉搜索树 链接&#xff1a;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进行下载三、结论解决方法前言 在项目上线前夕&#xff0c;临时添加了个数据导出的接口&#xff0c;需求是导出压缩包&#xff0c;选择了项…...

python中savgol_filter的详细解释

目录savgol_filter简介savgol_filter原理参数window_length对平滑的效果参数polyorder的平滑效果savgol_filter简介 Savitzky-Golay滤波器最初由Savitzky和Golay于1964年提出&#xff0c;是光谱预处理中常用滤波方法&#xff0c;它的核心思想是对一定长度窗口内的数据点进行k阶…...

C语言--指针进阶1

目录回顾字符指针指针数组数组指针&数组名和数组名的区别数组指针的使用指针作为形参练习数组参数、指针参数一维数组传参二维数组传参一级指针传参二级指针传参回顾 指针的内容&#xff0c;我们在初级阶段已经有所涉及了&#xff0c;我们先来复习一下 指针就是个变量&am…...

ssh的使用

Halo&#xff0c;这里是Ppeua。平时主要更新C语言&#xff0c;C&#xff0c;数据结构算法......感兴趣就关注我吧&#xff01;你定不会失望。 &#x1f308;个人主页&#xff1a;主页链接 &#x1f308;算法专栏&#xff1a;专栏链接 我会一直往里填充内容哒&#xff01; &…...

Apache Hadoop生态-目录汇总-持续更新

目录 1&#xff1a;系统服务分布图 3台分布式架构 1台单机架构 服务版本介绍 2&#xff1a;服务目录 存储相关 数据采集 任务调度 即席查询 数据可视化 集群监控 元数据管理 用户认证 权限管理 第三方windows客户端 1&#xff1a;系统服务分布图 3台分布式架构…...

「JVM 编译后话」编译器优化技术

后端编译&#xff08;即时编译、提前编译&#xff09;的目标时将字节码翻译成本地机器码&#xff0c;而难点是输出优化质量较高的机器码&#xff1b; 文章目录1. 优化技术概览2. 方法内联&#xff08;Inlining&#xff09;3. 逃逸分析&#xff08;Escape Analysis&#xff09;4…...

【python学习笔记】:输出与输入

01 输出方式 表达式语句、print()函数和使用文件对象的write()方法。 02 输出形式 格式化输出str.format()函数、转成字符串可以使用repr()或str()函数来实现。 (1)repr()&#xff1a;产生一个解释器易读的表达形式&#xff0c;便于字符串的拼接。 例&#xff1a;输出平方与…...

汽车电子社区交流宣传

http://t.csdn.cn/VSLO0http://t.csdn.cn/VSLO0 当今的汽车行业已经进入了数字化时代&#xff0c;汽车电子软件的开发变得越来越重要。在这个领域&#xff0c;开发者们需要应对各种挑战&#xff0c;包括复杂的硬件和软件交互、高效的嵌入式编程和安全性要求。为了帮助汽车电子…...

String、StringBuilder 和 StringBuffer 详解

碎碎念 这是一道老生常谈的问题了&#xff0c;字符串是不仅是 Java 中非常重要的一个对象&#xff0c;它在其他语言中也存在。比如 C、Visual Basic、C# 等。字符串使用 String 来表示&#xff0c;字符串一旦被创建出来就不会被修改&#xff0c;当你想修改StringBuffer 或者是 …...

windows服务器上传文件解决方案

1.说明 1.如果上传到linux系统&#xff0c;通常使用ftp相关技术&#xff0c;配合windows端的ftp客户端工具比如FileZilla等进行大文件的上传工作。 2.同理windows服务器也可以开启ftp服务用来传输大文件。 3.本文介绍偷懒方式&#xff08;常规是开启windows的ftp服务&#xff0…...

Android Studio翻译插件推介(Translation)

前言 Android Studio翻译插件适合英语水平不太好的程序员&#xff08;比如&#xff1a;我&#xff09;&#xff0c;最常用的翻译插件Translation和AndroidLocalize&#xff0c;本文主要讲解Translation&#xff0c;亲测可用。 先看看效果&#xff1a;这里是Android的API,任意选…...

DNS,DNS污染劫持,DNS加密

1. DNS&#xff08;Domain Name System&#xff09;DNS&#xff08;Domain Name System&#xff09;&#xff0c; 也叫网域名称系统&#xff0c;是互联网的一项服务。它实质上是一个 域名 和 IP 相互映射的分布式数据库.DNS&#xff08;Domain Name Server&#xff0c;域名服务…...

【Python】如何度量优秀代码——静态分析工具

静态分析工具背景有哪些静态分析工具呢度量Python代码的静态属性度量Python的生态系统代码的坏味道在类层面上在方法层面上结语背景 静态代码分析工具能够提炼出丰富的代码静态属性信息&#xff0c;这使得程序员可以对代码的复杂性、可修改性和可读性有进一步的了解。 有哪些…...

Open3D 点云高程归一化(基于2维地面点,Python版本)

文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 之前的博客中Open3D 点云高程归一化(基于地面点,Python版本)是基于三维空间进行最近地面点的查询操作,这里对其进行修改一下,将点云投影到水平面,基于二维空间进行最近地面点的查询,这种方式对一些较为陡峭的…...

动态系统的建模与分析

前言 CS小菜鸡控制理论入门 视频学习笔记 视频传送门&#xff1a;动态系统的建模与分析】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分析的,抓包器一个要几十万,学会这个功能就相当…...

Redis四 原理篇

《Redis四 原理篇》 提示: 本材料只做个人学习参考,不作为系统的学习流程,请注意识别!!! 《Redis四 原理篇》《Redis四 原理篇》1、原理篇-Redis数据结构1.1 Redis数据结构-动态字符串1.2 Redis数据结构-intset1.3 Redis数据结构-Dict1.4 Redis数据结构-ZipList1.4.1 Redis数据…...

从0开始写Vue项目-Vue实现数据渲染和数据的增删改查

从0开始写Vue项目-环境和项目搭建_慕言要努力的博客-CSDN博客从0开始写Vue项目-Vue2集成Element-ui和后台主体框架搭建_慕言要努力的博客-CSDN博客从0开始写Vue项目-Vue页面主体布局和登录、注册页面_慕言要努力的博客-CSDN博客从0开始写Vue项目-SpringBoot整合Mybatis-plus实现…...

AI技术的发展,人工智能对我们的生活有那些影响?

人工智能&#xff08;Artificial Intelligence&#xff09;&#xff0c;英文缩写为AI。它是研究、开发用于模拟、延伸和扩展人的智能的理论、方法、技术及应用系统的一门新的技术科学。 人工智能是计算机科学的一个分支&#xff0c;它企图了解智能的实质&#xff0c;并生产出一…...

Unity中的Mathf数学运算讲解(值得收藏)

Unity中的Mathf数学运算有哪些&#xff1f; Mathf.Abs(f)绝对值 计算并返回指定参数 f 绝对值 例如&#xff1a; // 输出 10 Debug.log(Mathf.Abs(-10)) Debug.log(Mathf.Abs(10))Mathf.Sin正弦 static function Sin (f : float) : float 计算并返回以弧度为单位指定的角 f 的…...

ABBYY FineReader16最新PDF图片文字识别软件

ABBYY FineReader16是非常好的一款 OCR 识别软件&#xff08;可以识别不可编辑的PDF和图片文件&#xff09;&#xff0c;操作非常简单。ABBYY FineReader 16是一款知名的OCR文字识别软件&#xff08;图片文字识别&#xff09;。ABBYY 16采用了ABBYY最新推出的基于AI的OCR技术&a…...

Leetcode14. 最长公共前缀

一、题目描述&#xff1a; 编写一个函数来查找字符串数组中的最长公共前缀。 如果不存在公共前缀&#xff0c;返回空字符串 “”。 示例 1&#xff1a; 输入&#xff1a;strs [“flower”,“flow”,“flight”] 输出&#xff1a;“fl” 示例 2&#xff1a; 输入&#xff1a;…...

JTT808jt1078

前言 交通部与2016年10月份推出了JT/T 1078-2016标准&#xff0c;全称是<道路运输车辆卫星定位系统视频通信协议> JTT808 808消息头内容如下表所示&#xff1a; 起始字节字段数据类型描述及要求0消息IDWORD2消息体属性WORD消息体属性格式结构图见图24终端手机号BCD[6…...

数字孪生加持,水利水电工程或将实现全生命周期管理

水利水电工程在数字孪生技术的加持&#xff0c;使得建设和运营更加高效和智能化&#xff0c;将工程中各种元素、过程和系统数字化&#xff0c;并建立数字孪生模型&#xff0c;以实现工程建设和运营的智能化管理。数字孪生对水利水电实现对工程建设的全生命周期管理&#xff0c;…...

RA4M2开发(3)----读取ISL29035数据,并在OLED上显示,串口打印

概述 HS3003是一种数字式温湿度传感器&#xff0c;可以测量环境中的温度和湿度。读取HS3003的数据需要连接传感器到一个数据采集系统&#xff0c;一般是微处理器或者单片机。以下是一个简单的读取HS3003数据的概述&#xff1a; 连接电路&#xff1a;将HS3003传感器连接到微处…...

密码复杂度

检查账户认证失败次数限制 修复建议&#xff1a; 配置SSH方式账户认证失败次数限制 编辑/etc/pam.d/sshd文件 在auth行下方添加&#xff1a; auth required pam_tally.so deny5 unlock_time600 no_lock_time 在account行下方添加&#xff1a; account required pam_tally.s…...