设计模式(十八)行为型:中介者模式详解

中介者模式(Mediator Pattern)是 GoF 23 种设计模式中的行为型模式之一,其核心价值在于通过引入一个中介者对象来封装一组对象之间的交互,从而降低对象间的耦合度,使对象不必显式地相互引用,实现松耦合的协作关系。它将原本“网状”的多对多通信结构转化为“星型”结构,所有对象仅与中介者通信,由中介者负责协调和转发消息。中介者模式广泛应用于图形用户界面(GUI)组件交互、聊天室系统、航空交通管制、多玩家游戏协调、微服务编排、事件总线(Event Bus)等需要集中管理复杂交互逻辑的场景,是构建可维护、可扩展、高内聚低耦合系统的架构利器。

一、详细介绍

中介者模式解决的是“多个对象之间存在复杂的、动态的、多对多的交互关系,导致系统结构混乱、难以理解、修改和扩展”的问题。在传统设计中,对象(如窗口中的按钮、文本框、下拉框)为了响应彼此的状态变化,会直接持有对方的引用并调用其方法。这导致:

  • 紧耦合:对象之间相互依赖,修改一个对象可能影响多个其他对象。
  • 高复杂度:交互逻辑分散在各个对象中,难以维护。
  • 难以复用:对象无法独立使用,必须依赖特定的协作环境。
  • 扩展困难:新增对象或交互逻辑需要修改大量现有代码。

中介者模式的核心思想是:“将交互逻辑集中化”,引入一个“中介者”(Mediator)作为所有交互的协调中心。各个协作对象(Colleague)不再直接通信,而是将请求发送给中介者,由中介者根据当前系统状态决定如何处理或转发该请求。

该模式包含以下核心角色:

  • Mediator(中介者接口):定义同事对象用来与中介者通信的接口,通常包含一个或多个通知方法(如 notify(sender, event))。
  • ConcreteMediator(具体中介者):实现 Mediator 接口,知道所有具体同事对象,并负责协调它们之间的交互。它维护同事对象的引用,并在收到通知时执行相应的协调逻辑。
  • Colleague(同事类):定义同事对象的基类或接口,持有对中介者的引用。同事对象通过中介者与其他同事通信。
  • ConcreteColleague(具体同事类):继承或实现 Colleague,在自身状态发生变化时,通过中介者发出通知,而不直接调用其他同事的方法。

中介者模式的关键优势:

  • 降低耦合度:同事对象之间无直接依赖,仅依赖中介者。
  • 集中控制交互逻辑:交互逻辑集中在中介者中,易于理解、修改和扩展。
  • 提高可复用性:同事对象可以独立于其他同事被复用。
  • 支持动态配置:中介者可以在运行时动态改变对象间的协作方式。
  • 简化对象设计:同事对象无需维护与其他对象的引用。

与“观察者模式”相比,中介者关注多对象间的双向协调,观察者关注一对多的单向状态通知;与“命令模式”相比,命令封装单个请求,中介者协调多个对象的协作;与“外观模式”相比,外观为子系统提供统一接口,中介者管理子系统内部组件的交互

中介者模式适用于:

  • 对象间交互复杂且多变。
  • 交互逻辑需要集中管理。
  • 希望提高对象的独立性和可复用性。

二、中介者模式的UML表示

以下是中介者模式的标准 UML 类图:

implements
implements
implements
knows
knows
uses
uses
«interface»
Mediator
+notify(sender: Colleague, event: String)
ConcreteMediator
-colleague1: ConcreteColleague1
-colleague2: ConcreteColleague2
+notify(sender: Colleague, event: String)
Colleague
-mediator: Mediator
+send(event: String)
+receive(event: String)
ConcreteColleague1
+send(event: String)
+receive(event: String)
ConcreteColleague2
+send(event: String)
+receive(event: String)

图解说明

  • Mediator 定义通知接口。
  • ConcreteMediator 知道所有具体同事,并实现协调逻辑。
  • Colleague 持有中介者引用,通过 send() 发送事件。
  • 同事对象通过中介者间接通信。

三、一个简单的Java程序实例及其UML图

以下是一个 GUI 对话框的示例,包含“登录”按钮、“用户名”文本框、“密码”文本框和“记住我”复选框。它们之间的交互由中介者协调。

Java 程序实例
// 中介者接口
interface Mediator {void notify(Component sender, String event);
}// 抽象同事组件
abstract class Component {protected Mediator mediator;public Component(Mediator mediator) {this.mediator = mediator;}public void send(String event) {mediator.notify(this, event);}public abstract void receive(String event);
}// 具体同事:按钮
class Button extends Component {private boolean enabled = false;private String name;public Button(Mediator mediator, String name) {super(mediator);this.name = name;}public void setEnabled(boolean enabled) {this.enabled = enabled;System.out.println("🔘 [" + name + "] 按钮状态: " + (enabled ? "启用" : "禁用"));}public boolean isEnabled() {return enabled;}@Overridepublic void receive(String event) {if ("enable".equals(event)) {setEnabled(true);} else if ("disable".equals(event)) {setEnabled(false);}}
}// 具体同事:文本框
class TextBox extends Component {private String text = "";private String name;public TextBox(Mediator mediator, String name) {super(mediator);this.name = name;}public void setText(String text) {this.text = text;System.out.println("📝 [" + name + "] 输入: \"" + text + "\"");// 文本变化时通知中介者send("textChanged");}public String getText() {return text;}@Overridepublic void receive(String event) {// 文本框通常不接收外部控制事件System.out.println("📝 [" + name + "] 收到事件: " + event + " (忽略)");}
}// 具体同事:复选框
class Checkbox extends Component {private boolean checked = false;private String name;public Checkbox(Mediator mediator, String name) {super(mediator);this.name = name;}public void setChecked(boolean checked) {this.checked = checked;System.out.println("☑️ [" + name + "] 状态: " + (checked ? "选中" : "未选中"));// 状态变化时通知中介者send("checkboxChanged");}public boolean isChecked() {return checked;}@Overridepublic void receive(String event) {// 复选框通常不接收外部控制事件System.out.println("☑️ [" + name + "] 收到事件: " + event + " (忽略)");}
}// 具体中介者:登录对话框
class LoginDialogMediator implements Mediator {private Button loginButton;private TextBox usernameBox;private TextBox passwordBox;private Checkbox rememberMeBox;public LoginDialogMediator(Button loginButton, TextBox usernameBox, TextBox passwordBox, Checkbox rememberMeBox) {this.loginButton = loginButton;this.usernameBox = usernameBox;this.passwordBox = passwordBox;this.rememberMeBox = rememberMeBox;}@Overridepublic void notify(Component sender, String event) {System.out.println("🔄 中介者收到事件: [" + sender.getClass().getSimpleName() + "] 发出 '" + event + "'");// 根据发送者和事件类型协调交互if (sender == usernameBox && "textChanged".equals(event)) {checkLoginButton();} else if (sender == passwordBox && "textChanged".equals(event)) {checkLoginButton();} else if (sender == rememberMeBox && "checkboxChanged".equals(event)) {if (rememberMeBox.isChecked()) {System.out.println("💡 用户选择记住登录状态");} else {System.out.println("💡 用户取消记住登录状态");}}}// 检查是否启用登录按钮private void checkLoginButton() {boolean usernameFilled = usernameBox.getText().trim().length() > 0;boolean passwordFilled = passwordBox.getText().trim().length() > 0;if (usernameFilled && passwordFilled) {loginButton.send("enable"); // 通过中介者启用按钮} else {loginButton.send("disable");}}// 提供获取组件的方法(可选,用于初始化)public Button getLoginButton() { return loginButton; }public TextBox getUsernameBox() { return usernameBox; }public TextBox getPasswordBox() { return passwordBox; }public Checkbox getRememberMeBox() { return rememberMeBox; }
}// 客户端使用示例
public class MediatorPatternDemo {public static void main(String[] args) {System.out.println("🔐 登录对话框 - 中介者模式示例\n");// 创建同事对象Button loginButton = new Button(null, "登录"); // 初始中介者为 nullTextBox usernameBox = new TextBox(null, "用户名");TextBox passwordBox = new TextBox(null, "密码");Checkbox rememberMeBox = new Checkbox(null, "记住我");// 创建中介者并注入同事对象LoginDialogMediator mediator = new LoginDialogMediator(loginButton, usernameBox, passwordBox, rememberMeBox);// 将中介者注入到同事对象中loginButton.mediator = mediator;usernameBox.mediator = mediator;passwordBox.mediator = mediator;rememberMeBox.mediator = mediator;// 初始状态:登录按钮应禁用System.out.println("\n--- 初始化 ---");loginButton.send("disable");// 模拟用户输入System.out.println("\n--- 用户输入用户名 ---");usernameBox.setText("alice");System.out.println("\n--- 用户输入密码 ---");passwordBox.setText("secret123");// 此时登录按钮应被启用System.out.println("\n✅ 用户名和密码已填写,登录按钮已启用");// 模拟取消记住我System.out.println("\n--- 用户取消记住我 ---");rememberMeBox.setChecked(false);// 模拟清空密码System.out.println("\n--- 用户清空密码 ---");passwordBox.setText("");// 登录按钮应被禁用System.out.println("\n✅ 密码已清空,登录按钮已禁用");}
}
实例对应的UML图(简化版)
implements
extends
extends
extends
has
has
has
«interface»
Mediator
+notify(sender: Component, event: String)
Component
-mediator: Mediator
+send(event: String)
+receive(event: String)
Button
-enabled: boolean
-name: String
+setEnabled(enabled: boolean)
+receive(event: String)
TextBox
-text: String
-name: String
+setText(text: String)
+receive(event: String)
Checkbox
-checked: boolean
-name: String
+setChecked(checked: boolean)
+receive(event: String)
LoginDialogMediator
-loginButton: Button
-usernameBox: TextBox
-passwordBox: TextBox
-rememberMeBox: Checkbox
+notify(sender: Component, event: String)
+checkLoginButton()

运行说明

  • LoginDialogMediator 是具体中介者,协调四个 GUI 组件。
  • 当用户名或密码框内容变化时,发送 textChanged 事件。
  • 中介者收到事件后,检查两个框是否都非空,决定启用或禁用登录按钮。
  • “记住我”复选框状态变化时,中介者记录用户选择。
  • 所有交互逻辑集中在中介者中,组件之间无直接引用。

四、总结

特性说明
核心目的集中管理对象间交互,降低耦合
实现机制星型通信结构,中介者协调转发
优点降低耦合、集中控制、提高复用性、简化对象
缺点中介者可能变得庞大复杂(“上帝对象”)、增加系统抽象层次
适用场景GUI 交互、聊天室、游戏协调、工作流引擎、事件总线
不适用场景对象间交互简单、交互逻辑稳定、性能极度敏感

中介者模式使用建议

  • 避免中介者过度膨胀,可将其拆分为多个子中介者。
  • 可结合“观察者模式”实现事件通知机制。
  • 在 Java 中,java.util.Timer/TimerTask 或事件总线(如 EventBus)是中介者思想的体现。
  • 微服务中的 API Gateway 或 Service Mesh 控制平面是分布式中介者。

架构师洞见:
中介者模式是“集中式协调”与“解耦通信”的哲学体现。在现代架构中,其思想已演变为事件驱动架构(EDA)服务网格(Service Mesh)API 网关消息中间件 的核心。例如,在微服务中,服务网格的 Sidecar 代理作为中介者,管理服务间的通信、熔断、重试;API 网关作为客户端请求的中介者,负责路由、认证、限流;Kafka 或 RabbitMQ 作为消息中介者,解耦生产者与消费者。

未来趋势是:中介者将与AI 编排引擎结合,AI 动态决策服务调用链;在边缘计算中,边缘网关作为本地服务的中介者;在量子网络中,量子中继器是量子态传输的中介者;在元宇宙中,虚拟世界的状态同步依赖于强大的中介协调系统。

掌握中介者模式,有助于设计出高内聚、低耦合、易维护的复杂交互系统。作为架构师,应在面对“多对象协作”或“交互逻辑复杂”时,主动考虑引入中介者。中介者不仅是模式,更是系统治理的枢纽——它提醒我们:真正的可扩展性,来自于将“混乱的网状通信”转化为“有序的星型控制”,让复杂性被封装在单一的协调点,而非散布在系统的每一个角落。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/916639.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/916639.shtml
英文地址,请注明出处:http://en.pswp.cn/news/916639.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Upload-Labs通关全攻略详细版

前端校验绕过:pass 01 两种思路:1.通过抓包,修改后缀 2.前端禁用js绕过前端后缀检验 首先写一个木马,改为图片格式GIF89a<?php eval($_POST[cmd])?>抓包之后改为PHP格式: 使用蚁剑连接木马,第一次尝试一直是返回数据为空,原因是没有链接到木马,于是寻找木马地址…

C#观察者模式示例代码

using System; using System.Collections.Generic; using System.Threading;namespace RefactoringGuru.DesignPatterns.Observer.Conceptual {// Observer观察者 也可以叫做订阅者 subscriberspublic interface IObserver{// Receive update from subject// 接收来自主题的更新…

电子电子架构 --- 软件项目的开端:裁剪

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 简单,单纯,喜欢独处,独来独往,不易合同频过着接地气的生活,除了生存温饱问题之外,没有什么过多的欲望,表面看起来很高冷,内心热情,如果你身…

Open CV图像基本操作可莉版

Open CV图像基本操作一、处理单个像素值访问像素值修改像素值二、处理单个ROI区域&#xff08;自选区域&#xff09;提取 ROI修改 ROI三、 处理图像通道通道分离通道合并四、处理整体图像缩放图像旋转图像平移图像翻转一、处理单个像素值 图像是由像素组成的矩阵&#xff0c;每…

k8s:将打包好的 Kubernetes 集群镜像推送到Harbor私有镜像仓库

本文介绍了在离线环境中部署Harbor镜像仓库的完整流程。首先通过脚本创建多个Harbor项目&#xff0c;然后使用KubeKey工具将预打包的Kubernetes镜像(kubesphere.tar.gz)推送到Harbor仓库。接着配置containerd以支持从私有仓库拉取镜像&#xff0c;包括设置TLS证书和镜像仓库端点…

IntelliJ IDEA中管理多版本Git子模块的完整指南

1.背景介绍项目是父子工程。父工程XXX-ZZZ-CCC。子模块XXX-api在线上git网站管理,有多个分支版本。现在需要接收别人代码&#xff0c;导入到本机管理。可以实现本机切换&#xff0c;修改&#xff0c;上传。2.创建本地仓库并拉取所有版本2.1.创建目录在D:\ideaworkspace\midend-…

Android ADB命令之内存统计与分析

一、核心命令总览工具 / 命令用途示例adb shell dumpsys meminfo查看设备全局内存状态adb shell dumpsys meminfoadb shell dumpsys meminfo <package>获取应用详细内存分类统计adb shell dumpsys meminfo com.example.appadb shell top动态查看进程内存和 CPU 占用adb s…

算法思维进阶 力扣 300.最长递增子序列 暴力搜索 记忆化搜索 DFS 动态规划 C++详细算法解析 每日一题

目录零、题目描述一、为什么这道题值得你深入理解&#xff1f;二、题目拆解&#xff1a;提取核心关键点三、明确思路&#xff1a;从暴力到优化的完整进化3. 进一步优化&#xff1a;动态规划&#xff08;自底向上递推&#xff09;4. 终极优化&#xff1a;贪心 二分查找&#xf…

图解网络-小林coding笔记(持续更新)

大纲 #mermaid-svg-trl6Q4B1uDO1z05w {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-trl6Q4B1uDO1z05w .error-icon{fill:#552222;}#mermaid-svg-trl6Q4B1uDO1z05w .error-text{fill:#552222;stroke:#552222;}#merm…

安宝特案例丨AR+AI+SOP?3大技术融合革新军工航天领域

军工、航空、航天领域存在 “小批量、多品种、依赖人工经验装配”的特性&#xff0c;这长期制约着生产效率与产品质量的提升。 技术融合应用案例 1 Arbigtec 装配效率提升类&#xff1a; 某型导弹制导系统装配&#xff1a;采用 AR 眼镜与 AI 视觉引导系统&#xff0c;200 精…

ip link show 查看/配置网络接口

ip link show&#xff08;或简写为 ip link&#xff09;是 Linux 系统中用于查看和配置网络接口&#xff08;网卡、虚拟接口等&#xff09;的命令&#xff0c;属于 iproute2 工具集的一部分。它是现代 Linux 系统中替代传统 ifconfig 命令的更强大工具。命令详解 基本语法 ip l…

电科金仓新一代数据库一体机:以 “云数据库 - AI 版” 引领 AI 时代数据库变革

前言 AI时代的数据库一体机市场&#xff0c;只能用两个词来形容&#xff1a;高手云集&#xff0c;战况激烈&#xff01; 国际巨头仍在高端市场占据主导地位&#xff0c;但在国产替代的冲击下&#xff0c;也开始另寻突破口&#xff1b;国内科技大厂攻势迅猛&#xff0c;通过开源…

IT运维的365天--033 跨交换机部署没有单独供电口的爱快AP到另一个地方去

前情提要&#xff1a;由于工作需要&#xff0c;领导要求在车间也添加一个无线网络供员工和设备使用&#xff0c;之前公司已经有一个爱快网络供员工使用&#xff0c;且物理隔绝部署在集团办公楼这边了。我一向是不喜欢碰到一个小事就拉一条网线&#xff0c;那样不得搞的跟蜘蛛网…

Flutter开发实战之路由与导航

第5章:路由与导航 在移动应用开发中,页面间的跳转是最基本也是最重要的功能之一。就像我们在现实生活中需要从一个房间走到另一个房间一样,在App中,用户需要在不同的界面间自由切换。Flutter提供了强大而灵活的路由系统来管理这些页面跳转,本章将深入探讨Flutter的路由与…

Android 图像编辑实战指南:从基础操作到进阶效果

在移动应用中&#xff0c;图像编辑功能已成为标配 —— 社交 APP 需要裁剪头像&#xff0c;电商 APP 需要给商品图加水印&#xff0c;工具 APP 需要提供滤镜效果。看似简单的 “裁剪”“缩放” 背后&#xff0c;实则涉及 Bitmap 像素操作、内存管理、性能优化等核心技术。很多开…

Java从入门到精通!第十八天(JDK17安装以及网络编程) 完结篇!!!

三、网络编程1&#xff0e;网络编程概述Java 是 Internet 上的语言&#xff0c;它从语言级上提供了对网络应用程序的支持&#xff0c;程序员能够很容易开发常见的网络应用程序。2&#xff0e;网络的基础&#xff08;1&#xff09;计算机网络把分布在不同地理区域的计算机与专门…

C++ STL常用容器总结(vector, deque, list, map, set)

C STL常用容器总结&#xff08;vector, deque, list, map, set&#xff09;1. vector&#xff08;动态数组&#xff09;特点定义和初始化常用操作遍历方法2. deque&#xff08;双端队列&#xff09;特点定义和初始化常用操作3. list&#xff08;双向链表&#xff09;特点定义和…

智能小车(F103C8T6)RT-THREAD版

前言 前面几章学会了PWM,超声波等&#xff0c;现在刚好结合起来控制智能小车 1&#xff1a;环境 KEIL5.38 RT-THREAD 3.1.3 STM32F103C8T6 2&#xff1a;硬件配件&#xff08;原来网上买的一套&#xff09; STM32F103C8T6 一个 MCU底板 一个 SG90 舵机 一个 红外避障 2个 hc-…

Linux 远程连接与文件传输:从基础到高级配置

Linux 远程连接与文件传输&#xff1a;从基础到高级配置 在 Linux 系统管理中&#xff0c;远程连接和文件传输是核心技能。SSH 协议提供了安全的远程访问方式&#xff0c;而基于 SSH 的 SFTP 和 SCP 则解决了跨服务器文件传输的需求。下面将详细解析 SSH 服务配置、三种远程操作…

17. 如何修改 flex 主轴方向

总结 flex-direction: row | row-reverse | column | column-reverse;一、作用说明 在 Flex 布局中&#xff0c;默认的主轴&#xff08;main axis&#xff09;方向是 水平向右&#xff08;即 row&#xff09;。 通过设置 flex-direction 属性&#xff0c;可以灵活改变主轴的方向…