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

十九、中介者模式

文章目录

  • 1 基本介绍
  • 2 案例
    • 2.1 Developer 抽象类
    • 2.2 FrontendDeveloper 类
    • 2.3 BackendDeveloper 类
    • 2.4 Mediator 接口
    • 2.5 ProjectManager 类
    • 2.6 Client 类
    • 2.7 Client 类的运行结果
    • 2.8 总结
  • 3 各角色之间的关系
    • 3.1 角色
      • 3.1.1 Colleague ( 同事 )
      • 3.1.2 ConcreteColleague ( 具体的同事 )
      • 3.1.3 Mediator ( 中介者 )
      • 3.1.4 ConcreteMediator ( 具体的中介者 )
      • 3.1.5 Client ( 客户端 )
    • 3.2 类图
  • 4 注意事项
  • 5 在框架中的使用
  • 6 优缺点
  • 7 适用场景
  • 8 总结


1 基本介绍

中介者模式(Mediator Pattern)是一种 行为型 设计模式,它降低了多个对象和类之间的 通信复杂性,通过引入一个中介对象来 封装 一系列对象之间的 交互使得这些对象不需要直接相互引用

2 案例

本案例让甲方将需求告知项目经理,然后项目经理将需求分发给前、后端开发人员,前、后端开发人员负责根据需求修改各自负责的代码。

2.1 Developer 抽象类

public abstract class Developer { // 开发人员// 由于其子类的逻辑十分简单,无需向 mediator 发送消息,所以其子类没有直接使用 mediatorprotected Mediator mediator; // 对应的中介者public Developer(Mediator mediator) {this.mediator = mediator;}protected abstract void modifyCode(String require); // 根据需求修改代码
}

2.2 FrontendDeveloper 类

public class FrontendDeveloper extends Developer { // 前端开发人员public FrontendDeveloper(Mediator mediator) {super(mediator);}@Overrideprotected void modifyCode(String require) {System.out.println("前端开发人员根据需求「" + require + "」修改前端显示的代码");}
}

2.3 BackendDeveloper 类

public class BackendDeveloper extends Developer { // 后端开发人员public BackendDeveloper(Mediator mediator) {super(mediator);}@Overrideprotected void modifyCode(String require) {System.out.println("后端开发人员根据需求「" + require + "」修改前端显示的代码");}
}

2.4 Mediator 接口

// 实现这个接口后,可以当项目的中介者,接收甲方的需求,通知前、后端开发人员修改代码
public interface Mediator {void organizeMembers(); // 组织开发组的成员void changeRequire(String require); // 甲方更改需求
}

2.5 ProjectManager 类

public class ProjectManager implements Mediator { // 项目经理// 假设项目中只需要一个前端开发人员和一个后端开发人员private FrontendDeveloper frontendDeveloper;private BackendDeveloper backendDeveloper;@Overridepublic void organizeMembers() {// 组织了两个开发人员进行开发frontendDeveloper = new FrontendDeveloper(this);backendDeveloper = new BackendDeveloper(this);}@Overridepublic void changeRequire(String require) {System.out.println("项目经理收到甲方更改的需求「" + require + "」");frontendDeveloper.modifyCode(require);backendDeveloper.modifyCode(require);}
}

2.6 Client 类

public class Client { // 客户端,充当甲方,测试了项目经理更改需求的能力public static void main(String[] args) {Mediator pm = new ProjectManager();pm.organizeMembers();pm.changeRequire("增加一个 傻瓜式引导 的功能");pm.changeRequire("增加一个 个性化内容推荐 的功能");}
}

2.7 Client 类的运行结果

项目经理收到甲方更改的需求「增加一个 傻瓜式引导 的功能」
前端开发人员根据需求「增加一个 傻瓜式引导 的功能」修改前端显示的代码
后端开发人员根据需求「增加一个 傻瓜式引导 的功能」修改前端显示的代码
项目经理收到甲方更改的需求「增加一个 个性化内容推荐 的功能」
前端开发人员根据需求「增加一个 个性化内容推荐 的功能」修改前端显示的代码
后端开发人员根据需求「增加一个 个性化内容推荐 的功能」修改前端显示的代码

2.8 总结

在本案例中,甲方更改需求时,只需要通知项目经理即可,无需通知具体的开发人员,通知具体开发人员的任务由项目经理完成。此外,前后端开发人员员也不需要向对方提问应该如何达成需求,由项目经理来通知他们应该达成什么样的需求。这样就减少了对象之间的交流,只存在必要的交流,简化了对象之间的关系。

3 各角色之间的关系

3.1 角色

3.1.1 Colleague ( 同事 )

该角色负责 定义与 Mediator 角色进行通信的 接口。本案例中,Developer 抽象类扮演了该角色。

3.1.2 ConcreteColleague ( 具体的同事 )

该角色负责 实现 Colleague 角色定义的 接口。本案例中,FrontendDeveloper, BackendDeveloper 类都在扮演该角色。

3.1.3 Mediator ( 中介者 )

该角色负责 定义 创建 ConcreteColleague 角色的 接口 和 与其进行通信的 接口。本案例中,Mediator 接口扮演了该角色。

3.1.4 ConcreteMediator ( 具体的中介者 )

该角色负责 实现 Mediator 角色的接口。本案例中,ProjectManager 类扮演了该角色。

3.1.5 Client ( 客户端 )

该角色负责 创建 ConcreteMediator 角色的对象,并 使用它完成具体的业务逻辑。本案例中,Client 类扮演了该角色。

3.2 类图

alt text
说明:

  • Colleague 中的 controlColleague() 方法用于与 Mediator 通信,根据 Mediator 的指示修改自己的状态。
  • Mediator 可以是接口。
  • Colleague 也可以是接口,不过需要将其聚合的 Mediator 放到所有 ConcreteColleague 中。
  • 当 Colleague 是抽象类时,其 setMediator() 方法可以由构造器替换。

4 注意事项

  • 中介者责任重大:中介者对象承担了较多的责任,它是所有对象之间通信的桥梁。因此,一旦中介者出现了问题,整个系统就可能受到影响。所以,在设计中介者时,需要确保其 稳定性可靠性
  • 避免中介者对象复杂化:如果设计不当,中介者对象可能会变得过于复杂,难以理解和维护。在实际使用中,需要 特别注意中介者的设计避免其承担过多的责任和功能。可以通过合理划分中介者的职责、采用模块化设计等方法来降低其复杂度。
  • 系统灵活性的保持:虽然中介者模式提高了系统的灵活性,但 在设计时需要确保系统仍然能够应对未来的变化。例如,可以通过定义 可扩展的中介者接口、使用 策略模式 等方法来增强系统的可扩展性。
  • 性能考虑:在某些情况下,通过中介者进行 间接交互 可能比 直接交互 具有更高的性能开销。因此,在设计完成后,需要对系统的性能进行测试,并根据测试结果进行优化。例如,可以通过优化中介者的内部实现、减少不必要的通信等方法来提高系统性能。

5 在框架中的使用

在流行的 Java 框架中,使用了中介者模式的思想:

  • MVC 框架:在 MVC(Model-View-Controller)框架 中,控制器(Controller)充当了 模型(Model)和 视图(View)之间的中介者。控制器负责接收用户的输入,并将其转换为模型可以理解的格式,同时,当模型状态发生变化时,控制器也会负责通知视图进行更新。这种设计使得模型、视图和控制器之间的耦合度降低,提高了系统的可维护性和可扩展性。
  • 消息中间件:在分布式系统中,消息中间件(如 RabbitMQ、Kafka 等)也体现了中介者模式的思想。消息中间件 作为消息的 生产者消费者 之间的中介,负责消息的存储和转发,使得生产者和消费者之间不需要直接进行通信,从而降低了系统的耦合度,提高了系统的可靠性和可扩展性。

6 优缺点

优点

  • 解耦与结构转换多个类相互耦合 容易形成 网状结构,复杂且难以维护。使用中介者模式可以将 网状结构 分离为 星型结构,通过中介者对象进行通信,从而实现解耦。
  • 符合迪米特法则(降低对象间的耦合度):中介者模式使得 对象之间不需要知道彼此的具体实现细节,只需通过中介者进行通信,这符合 迪米特法则,有助于减少对象之间的 耦合度。这使得对象之间的依赖关系更加简单,系统的结构更加清晰。
  • 易于扩展和维护:当系统中需要增加新的对象或改变对象之间的交互方式时,只需修改中介者对象即可,而无需修改其他对象,这降低了系统 扩展维护 的复杂性。
  • 提高系统的灵活性:通过更换不同的中介者对象,可以灵活地改变对象之间的交互逻辑,从而增加系统的 灵活性

缺点

  • 中介者可能变得复杂:如果系统中存在大量的对象,并且这些对象之间的交互关系非常复杂,那么中介者对象可能会变得非常庞大和复杂。这会增加中介者对象的维护难度,并可能导致代码难以理解和修改。
  • 对中介者的依赖:由于 所有对象之间的交互都通过中介者进行,因此 系统对中介者的依赖程度较高。如果中介者出现故障或设计不当,可能会影响整个系统的正常运行。
  • 可能隐藏系统复杂性:中介者模式可能会 隐藏对象之间的 直接交互关系,使得系统的整体结构和交互逻辑变得不够直观。这可能会给开发人员带来一定的理解难度,尤其是在处理复杂的系统时。
  • 更高的性能开销:在某些情况下,通过中介者进行 间接交互 可能比 直接交互 具有 更高的性能开销。虽然这种开销在大多数情况下是可以接受的,但在性能敏感的应用中可能需要谨慎考虑。

7 适用场景

  • 引用关系复杂的系统
    • 系统中对象之间存在复杂的引用关系,且 这些引用关系导致系统结构混乱、难以理解 时,可以考虑使用中介者模式。通过引入中介者对象,可以封装对象之间的交互细节,降低系统的复杂度。
    • 例如,在 电商平台 中,卖家、买家、物流公司和支付平台之间可能存在复杂的交互关系,可以通过增加一个“消费中介”来分离它们之间的关系。
  • 需要改变行为的系统
    • 如果 系统中对象之间的交互行为需要频繁变更,且 这些变更会影响到多个对象 时,可以使用中介者模式。通过增加新的中介者类,可以轻松地实现行为的扩展和变更,而无需修改原有对象。
    • 例如,在 多人聊天室场景 中,如果需要变更消息的传递方式(如从文本消息变为语音消息),可以通过增加一个新的中介者类来实现,而无需修改用户对象。
  • 高耦合的系统
    • 系统中对象之间的耦合度较高,且 这种耦合度影响了系统的可维护性和可扩展性 时,可以考虑使用中介者模式。通过中介者对象来封装对象之间的交互,可以降低对象之间的耦合度,使系统更加灵活和易于维护。
    • 例如,在 MVC 框架中,控制器(Controller)作为模型(Model)和视图(View)之间的中介者,降低了它们之间的耦合度。当需要修改视图或模型的实现时,只需修改控制器即可,而无需修改其他组件。
  • 集中化交互管理的系统
    • 在某些场景中,可能 需要一个中心化的对象来管理多个对象之间的交互。此时,可以使用中介者模式来实现。中介者对象负责接收来自各个对象的请求,并根据一定的规则将这些请求转发给相应的对象进行处理。
    • 例如,在交通控制系统中,交通灯可以作为中介者对象来管理车辆和行人的交互。交通灯根据当前的道路情况和交通规则来控制车辆和行人的通行。

8 总结

中介者模式 是一种 行为型 设计模式,它通过引入 中介,组织了系统中各个对象之间的关系,从 网状关系 简化到 星型关系,降低了系统的耦合度,提高了系统的灵活性。不过,给对象之间添加一个中介会导致性能的下降,如果对性能要求很高,最好不要使用本模式。

相关文章:

十九、中介者模式

文章目录 1 基本介绍2 案例2.1 Developer 抽象类2.2 FrontendDeveloper 类2.3 BackendDeveloper 类2.4 Mediator 接口2.5 ProjectManager 类2.6 Client 类2.7 Client 类的运行结果2.8 总结 3 各角色之间的关系3.1 角色3.1.1 Colleague ( 同事 )3.1.2 ConcreteColleague ( 具体的…...

编程参考 - 头文件中使用static inline

在Linux kernel的头文件中,经常使用static inline来声明一个函数。 比如include/linux/delay.h中, static inline void ssleep(unsigned int seconds) { msleep(seconds * 1000); } static Keyword * 范围限制: 当应用于函数或变量时&#…...

Uniapp使用antd组件库

组件库官网 https://www.antdv.com/docs/vue/introduce-cn 安装 在命令行终端输入 npm uni --save ant-design-vue配置 我这里用的是uniapp的vue3版本模板 在main.js里面引入 只要改下面带序号的地方即可 import App from ./App// #ifndef VUE3 import Vue from vue im…...

计算机毕业设计选题推荐-高校实验室管理系统-Java/Python项目实战

✨作者主页:IT研究室✨ 个人简介:曾从事计算机专业培训教学,擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Python…...

nest定义响应码message文本

需求 需要对接口的异常响应码,手动设置message文本!!! 例如:项目中使用multer中间件实现文件上传,multer设置了文件大小限制,该中间件校验文件时错误(文件超出)会自动响…...

Java | Leetcode Java题解之第342题4的幂

题目: 题解: class Solution {public boolean isPowerOfFour(int n) {return n > 0 && (n & (n - 1)) 0 && n % 3 1;} }...

【日常开发】java中一个list对象集合 将字段a为 大豆 小麦 玉米等元素放在最前面 并组成新集合

🎈边走、边悟🎈迟早会好 在Java中实现这个功能,可以使用Stream来筛选出符合条件的元素,将它们放在新集合的前面,同时保留其他元素在新集合的后面。以下是如何实现的代码示例: 代码示例: impo…...

C++ 设计模式——原型模式

原型模式 原型模式主要组成部分原型模式的使用步骤原型模式的 UML 图原型模式 UML 图解析优点和缺点适用场景总结 原型模式 原型(Prototype)模式是一种创建型模式。原型模式通过(原型对象)克隆出对个一模一样的对象。实际上,该模式与其说是一种设计模式&#xff0c…...

【Harmony OS 4.0】待办列表案例

src/main/ets/example1/Models.ets // 定义class类数据模型 export class TaskDataModel {// private 私有属性&#xff0c;在类对象外不允许随意更改数据&#xff0c;必须本地初始化。private tasks: Array<string> [早起晨练, 准备早餐, 阅读名著, 学习ArkTs, 玩游戏…...

快速把文件名统计到excel表的方法

文件名统计到EXCEL表&#xff0c;这似乎很多人都没听说过&#xff0c;因为它与EXCEL表格不沾边&#xff0c;那么这个需求如何实现&#xff0c;用到什么方法&#xff0c;今天给大家介绍一个比较实用的方法&#xff0c;它可以把文件名或文件夹的名快速提取并统计到EXCEL表格上去。…...

开源通用验证码识别OCR —— DdddOcr 源码赏析(一)

文章目录 [toc] 前言DdddOcr环境准备安装DdddOcr使用示例 源码分析实例化DdddOcr实例化过程 分类识别分类识别过程 未完待续 前言 DdddOcr 源码赏析 DdddOcr DdddOcr是开源的通用验证码识别OCR 官方传送门 环境准备 安装DdddOcr pip install ddddocr使用示例 示例图片如…...

上升ECMAScript性能优化技巧与陷阱(下)

4. 深拷贝和浅拷贝的选择不当 在JavaScript中&#xff0c;对象是通过引用传递的&#xff0c;这意味着当你将一个对象赋值给另一个变量时&#xff0c;你实际上是在传递对象的引用&#xff0c;而不是对象本身。这导致了一个常见的问题&#xff1a;当你修改一个对象的属性时&…...

用7EPhone云手机进行TikTok的矩阵运营

“根据市局机构Statista发布的报告显示&#xff0c;截至2024年4月&#xff0c;TikTok全球下载量超过49.2亿次&#xff0c;月度活跃用户数超过15.82亿。TikTok的流量受欢迎程度可想而知&#xff0c;也一跃成为了全球第五大最受欢迎的社交APP。” 人群密集的地方社区也是适合推广…...

谷歌浏览器下载文件被阻止怎么解除

在工作生活中&#xff0c;我们会使用谷歌浏览器下载各种各样的文件&#xff0c;不过偶尔会遇到文件下载被阻止的情况。为了解决这一问题&#xff0c;本文为大家分享了实用的措施建议&#xff0c;一起来了解一下吧。&#xff08;本文由https://chrome.cmrrs.com/站点的作者进行编…...

apt E: 无法定位软件包 winehq-stable

执行了 添加wine源 wget -NP /etc/apt/sources.list.d/ https://dl.winehq.org/wine-builds/ubuntu/dists/jammy/winehq-jammy.sources还需要执行 更新源 apt update...

P2460[SDOI2007] 科比的比赛

第一次做洛谷系列&#xff0c;紧张&#xff0c;请多关照哦 题目传送门&#xff1a;[SDOI2007] 科比的比赛 - 洛谷 思路分析 这道题大概题意是给定我们的主人公 Kobe Bryant 的 mm 个对手&#xff0c;nn 场比赛相对应的获胜概率。求 Kobe Bryant 最大全部获胜概率和打败对手能…...

linux学习--第二天

--Linux文件系统 -显示文件命令 cat 1. cat -b 文件&#xff1a;从1开始对非空输出行编号 2. cat -n 文件&#xff1a;从1开始对所有行编号 3. cat -s 文件&#xff1a;将连续多行空白行合并 more&#xff08;显示一屏文本内容&#xff09; 1. more -num 文件&#xff…...

使用 Flask、Celery 和 Python 实现每月定时任务

为了创建一个使用 Flask、Celery 和 Python 实现的每月定时任务&#xff0c;我们需要按照以下步骤进行&#xff1a; 1.安装必要的库 我们需要安装 Flask、Celery 和 Redis&#xff08;作为消息代理&#xff09;。我们可以使用 pip 来安装它们&#xff1a; bash复制代码 ​ p…...

【c语言】整数在内存中的储存(大小端字节序)

整数在内存中的储存&#xff08;大小端字节序&#xff09; 1.整数在内存中的储存 2.大小端字节序 3.整数在内存中储存例子 4.字节序判断 5.死循环现象 文章目录 整数在内存中的储存&#xff08;大小端字节序&#xff09;整数在内存中的储存大小端字节序什么是大小端为什么会有…...

浅谈SIMD、向量化处理及其在StarRocks中的应用

前言 单指令流多数据流(SIMD)及其衍生出来的向量化处理技术已经有了相当的历史&#xff0c;并且也是高性能数据库、计算引擎、多媒体库等组件的标配利器。笔者在两年多前曾经做过一次有关该主题的内部Geek分享&#xff0c;但可能是由于这个topic离实际研发场景比较远&#xff0…...

RestClient

什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端&#xff0c;它允许HTTP与Elasticsearch 集群通信&#xff0c;而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级&#xff…...

云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地

借阿里云中企出海大会的东风&#xff0c;以**「云启出海&#xff0c;智联未来&#xff5c;打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办&#xff0c;现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...

IGP(Interior Gateway Protocol,内部网关协议)

IGP&#xff08;Interior Gateway Protocol&#xff0c;内部网关协议&#xff09; 是一种用于在一个自治系统&#xff08;AS&#xff09;内部传递路由信息的路由协议&#xff0c;主要用于在一个组织或机构的内部网络中决定数据包的最佳路径。与用于自治系统之间通信的 EGP&…...

vscode(仍待补充)

写于2025 6.9 主包将加入vscode这个更权威的圈子 vscode的基本使用 侧边栏 vscode还能连接ssh&#xff1f; debug时使用的launch文件 1.task.json {"tasks": [{"type": "cppbuild","label": "C/C: gcc.exe 生成活动文件"…...

关于nvm与node.js

1 安装nvm 安装过程中手动修改 nvm的安装路径&#xff0c; 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解&#xff0c;但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后&#xff0c;通常在该文件中会出现以下配置&…...

CentOS下的分布式内存计算Spark环境部署

一、Spark 核心架构与应用场景 1.1 分布式计算引擎的核心优势 Spark 是基于内存的分布式计算框架&#xff0c;相比 MapReduce 具有以下核心优势&#xff1a; 内存计算&#xff1a;数据可常驻内存&#xff0c;迭代计算性能提升 10-100 倍&#xff08;文档段落&#xff1a;3-79…...

Frozen-Flask :将 Flask 应用“冻结”为静态文件

Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是&#xff1a;将一个 Flask Web 应用生成成纯静态 HTML 文件&#xff0c;从而可以部署到静态网站托管服务上&#xff0c;如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...

关于 WASM:1. WASM 基础原理

一、WASM 简介 1.1 WebAssembly 是什么&#xff1f; WebAssembly&#xff08;WASM&#xff09; 是一种能在现代浏览器中高效运行的二进制指令格式&#xff0c;它不是传统的编程语言&#xff0c;而是一种 低级字节码格式&#xff0c;可由高级语言&#xff08;如 C、C、Rust&am…...

OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别

OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别 直接训练提示词嵌入向量的核心区别 您提到的代码: prompt_embedding = initial_embedding.clone().requires_grad_(True) optimizer = torch.optim.Adam([prompt_embedding...

vue3+vite项目中使用.env文件环境变量方法

vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量&#xff0c;这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...