深入理解设计模式之中介者模式:解耦对象交互的利器

为什么需要中介者?

在软件开发中,我们经常会遇到对象之间需要相互通信的场景。当系统规模较小时,对象直接相互引用并通信可能不会带来太大问题。但随着系统复杂度增加,对象间的交互关系会变得错综复杂,形成一个复杂的网状结构。这种高度耦合的设计会导致以下问题:

  1. 可维护性差:牵一发而动全身,修改一个对象可能需要修改多个相关对象

  2. 复用性低:由于对象间高度依赖,很难单独复用某个对象

  3. 扩展困难:新增或修改交互逻辑需要改动大量现有代码

中介者模式正是为解决这些问题而生的。它通过引入一个中介对象来封装对象间的交互,使对象间不再直接引用,而是通过中介者进行间接通信,从而将网状结构转化为星型结构,大大降低了系统的耦合度。

中介者模式的核心概念

模式定义

中介者模式(Mediator Pattern)的定义是:用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。

模式结构

中介者模式包含四个关键角色:

  1. Mediator(中介者接口)

    • 定义同事对象到中介者对象的接口

    • 通常包含一个或多个事件处理方法

  2. ConcreteMediator(具体中介者)

    • 实现中介者接口

    • 协调各同事对象的行为

    • 知道并维护它的各个同事

  3. Colleague(同事类接口)

    • 定义同事类的接口

    • 通常持有中介者的引用

  4. ConcreteColleague(具体同事类)

    • 实现同事类接口

    • 每个具体同事类只知道自己的行为

    • 需要与其他同事通信时,通过中介者转发

UML类图

┌─────────────┐       ┌─────────────┐
│   Mediator  │<------│  Colleague  │
└─────────────┘       └─────────────┘^                      ^|                      |
┌─────────────┐       ┌─────────────┐
│ConcreteMedi │       │ConcreteColl │
└─────────────┘       └─────────────┘

深入解析中介者模式

工作原理

  1. 对象注册:同事对象在创建时向中介者注册自己

  2. 通信机制:当同事对象需要与其他同事通信时,不是直接调用对方的方法,而是通知中介者

  3. 消息转发:中介者接收到消息后,根据业务逻辑决定如何转发给其他同事对象

  4. 解耦实现:同事对象之间不再相互持有引用,所有交互都通过中介者进行

代码实现详解

让我们通过一个完整的聊天室示例来深入理解中介者模式的实现:

// 1. 定义中介者接口
public interface ChatMediator {void sendMessage(String msg, User user);void addUser(User user);
}// 2. 实现具体中介者
public class ChatMediatorImpl implements ChatMediator {private List<User> users;public ChatMediatorImpl() {this.users = new ArrayList<>();}@Overridepublic void sendMessage(String msg, User user) {for (User u : this.users) {// 消息不发送给发送者自己if (u != user) {u.receive(msg);}}}@Overridepublic void addUser(User user) {this.users.add(user);}
}// 3. 定义同事类(用户)抽象
public abstract class User {protected ChatMediator mediator;protected String name;public User(ChatMediator med, String name) {this.mediator = med;this.name = name;}public abstract void send(String msg);public abstract void receive(String msg);
}// 4. 实现具体同事类
public class UserImpl extends User {public UserImpl(ChatMediator med, String name) {super(med, name);}@Overridepublic void send(String msg) {System.out.println(this.name + " 发送消息: " + msg);mediator.sendMessage(msg, this);}@Overridepublic void receive(String msg) {System.out.println(this.name + " 收到消息: " + msg);}
}// 5. 客户端使用
public class ChatClient {public static void main(String[] args) {ChatMediator mediator = new ChatMediatorImpl();User user1 = new UserImpl(mediator, "张三");User user2 = new UserImpl(mediator, "李四");User user3 = new UserImpl(mediator, "王五");mediator.addUser(user1);mediator.addUser(user2);mediator.addUser(user3);user1.send("大家好!");user2.send("欢迎新人!");}
}

输出结果

张三 发送消息: 大家好!
李四 收到消息: 大家好!
王五 收到消息: 大家好!
李四 发送消息: 欢迎新人!
张三 收到消息: 欢迎新人!
王五 收到消息: 欢迎新人!

中介者模式的优缺点

优点

  1. 降低耦合度:将对象间多对多的关系转化为一对多的关系,减少对象间的直接依赖

  2. 集中控制:将交互逻辑集中在中介者中,使交互行为更容易理解和维护

  3. 简化对象协议:用中介者和同事间的一对多交互替代了同事之间的多对多交互

  4. 提高灵活性:可以独立地改变和复用各个同事类和中介者类

  5. 减少子类生成:将原本分布于多个对象间的行为集中在一起,减少子类的生成

缺点

  1. 中介者可能变得复杂:随着交互逻辑的增加,中介者可能变得过于庞大和复杂

  2. 性能考虑:所有通信都通过中介者转发,可能带来一定的性能开销

  3. 单点故障风险:中介者成为系统的关键点,一旦出现问题会影响整个系统

中介者模式的应用场景

中介者模式特别适用于以下场景:

  1. GUI开发:各种UI组件(按钮、文本框等)之间的交互

  2. 聊天应用:多个用户之间的消息传递

  3. 航空管制系统:飞机与塔台之间的通信协调

  4. 事件分发系统:多个对象需要响应同一事件

  5. 工作流引擎:协调多个处理节点之间的交互

  6. 游戏开发:游戏对象之间的交互管理

实际应用案例

案例1:机场塔台控制系统

在航空领域,飞机之间的通信不是直接进行的,而是通过塔台这个中介者来协调:

// 中介者:塔台
public interface Tower {void registerFlight(Flight flight);void sendWarning(String message, Flight sender);
}// 具体同事:飞机
public abstract class Flight {protected Tower tower;protected String flightNumber;public Flight(Tower tower, String flightNumber) {this.tower = tower;this.flightNumber = flightNumber;tower.registerFlight(this);}public abstract void receiveWarning(String message);public abstract void sendWarning(String message);
}// 使用示例
public class AviationExample {public static void main(String[] args) {Tower tower = new ControlTower();Flight flight1 = new CommercialFlight(tower, "CA123");Flight flight2 = new CargoFlight(tower, "FX456");flight1.sendWarning("前方有气流,请注意");flight2.sendWarning("收到,正在调整高度");}
}

案例2:电子商务订单系统

在电商系统中,订单处理涉及多个组件(库存、支付、物流等),可以使用中介者协调:

public interface OrderMediator {void placeOrder(Order order);void cancelOrder(Order order);void processPayment(Order order);void updateInventory(Order order);void arrangeShipping(Order order);
}public class OrderProcessor implements OrderMediator {// 实现各种订单处理逻辑// 协调库存、支付、物流等子系统
}// 同事类:支付系统
public class PaymentSystem {private OrderMediator mediator;public void processPayment(Order order) {// 处理支付mediator.updateInventory(order);}
}

中介者模式与其他模式的关系

  1. 与外观模式

    • 相似点:都抽象了已有类的功能

    • 区别:外观模式是单向的,为子系统提供统一接口;中介者模式是双向的,协调多个对象间的交互

  2. 与观察者模式

    • 可以组合使用,中介者通常使用观察者模式来实现同事对象之间的通信

    • 观察者模式侧重一对多的依赖关系,中介者模式侧重对象间的交互解耦

  3. 与命令模式

    • 命令模式可用于实现中介者与同事之间的通信机制

  4. 与代理模式

    • 代理模式控制对一个对象的访问,而中介者模式控制多个对象间的交互

最佳实践与注意事项

  1. 避免过度使用:不要为了使用模式而使用模式,只有对象间交互确实复杂时才考虑中介者

  2. 保持中介者简洁:不要让中介者承担太多职责,必要时可以拆分多个中介者

  3. 合理设计接口:中介者接口应该足够抽象,不应依赖于具体同事类

  4. 性能优化:对于高频交互的场景,考虑引入缓存或批处理机制

  5. 异常处理:中介者应该妥善处理同事对象抛出的异常,避免影响整个系统

总结

中介者模式是一种强大的设计模式,它通过引入中介对象来解耦对象间的复杂交互。在适当的场景下使用中介者模式可以带来以下好处:

  • 使系统结构更加清晰

  • 降低组件间的耦合度

  • 提高代码的可维护性和可扩展性

  • 使交互逻辑更加集中和明确

然而,中介者模式也不是万能的。当交互逻辑简单时,使用中介者可能会引入不必要的复杂性。因此,作为开发者,我们需要根据实际场景权衡利弊,做出最合适的设计决策。

 

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

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

相关文章

从 0 安装 Label Studio:搭建可后台运行的数据标注平台(systemd 实践

本文将介绍如何使用 pip 安装 Label Studio&#xff0c;并通过 systemd 实现开机自启与后台运行&#xff0c;适用搭建个人项目的数据标注平台。 一、Label Studio 简介 Label Studio 是一个开源、跨模态的数据标注工具&#xff0c;支持文本、图像、音频、视频、HTML等多种类型…

【数据结构】链表(linked list)

目录 一、链表的介绍 二、单链表 1. 单链表的初始化 2. 单链表的插入 &#xff08;1&#xff09;动态申请一个节点 &#xff08;2&#xff09;头插法 &#xff08;3&#xff09;尾插法 &#xff08;4&#xff09;按照位置来插入 &#xff08;5&#xff09;在地址之前插…

反序列化漏洞1-PHP序列化基础概念(0基础超详细)

一.PHP序列化基础概念首先当我们看到反序列化漏洞这个概念&#xff0c;我们的第一个问题是什么是反序列化&#xff1f;那么我们要知道什么是反序列化就要知道什么是序列化。序列化就是可以将一个对象压缩并格式化成字符串&#xff0c;可以将该对象保存下来&#xff0c;以便存储…

【微服务】Ocelot微服务网关

目录 一、目的 二、Ocelot介绍 三、.Net中使用Ocelot搭建网关服务 3.1 搭建网关Ocelot步骤 3.1.1、创建Net7 WebApi服务 3.1.2、Nuget引入-Ocelot程序包&#xff08;版本&#xff1a;19.0.2&#xff09; 3.1.3、配置中间件和IOC注册 3.1.4 配置文件编辑Ocelot网关配置信…

零基础入门:用按键精灵实现视频自动操作(附完整脚本)

摘要&#xff1a;本文手把手教你编写视频平台的自动化脚本&#xff0c;涵盖点击、循环、防检测等核心技巧&#xff0c;无需编程基础&#xff0c;轻松实现自动播放/点赞/跳过广告。&#xff08;使用按键精灵2024版演示&#xff09; 一、应用场景 自动化操作&#xff1a;自动跳过…

AI(学习笔记第六课) 使用langchain进行AI开发 load documents(csv和文件夹)

文章目录AI(学习笔记第六课) 使用langchain进行AI开发 load documents(csv和文件夹)学习内容&#xff1a;1.load documents&#xff08;csv&#xff09;1.1 学习url1.2 load csv文件1.2.1 默认load1.2.2 csv文件内容1.2.2 执行csv文件的load1.3 Customizing the CSV parsing an…

企业运维实战:Jenkins 依赖 JDK21 与应用需 JDK1.8 共存方案(含流水线配置)

前言&#xff1a;在企业运维中&#xff0c;“工具升级”与“业务兼容”的平衡始终是核心挑战。近期我们遇到一个典型场景&#xff1a;Jenkins 升级到 2.450 版本后&#xff0c;强制要求 JDK21 运行环境&#xff1b;但开发团队的应用程序因框架依赖&#xff0c;必须使用 JDK1.8 …

爬虫小知识三:selenium库

前言 selenium 库是一种用于 Web 应用程序测试的工具&#xff0c;它可以驱动浏览器执行特定操作&#xff0c;自动按照脚本代码做出单击、输入、打开、验证等操作&#xff0c;支持的浏览器包括 IE、Firefox、Safari、Chrome、Opera 等。 与 requests 库不同的是&#xff0c;se…

Jmeter使用 -1

1 接口测试1.1 为什么要进行接口测试接口测试能够绕过前端校验&#xff0c;对后端的接口处理逻辑进行测试&#xff08;数据的边界/格式/类型&#xff09;在一些需要重复测试的需求中&#xff0c;接口自动化的效率比手工执行效率高1.2 接口测试流程熟悉API接口文档&#xff08;接…

GitHub 趋势日报 (2025年07月16日)

&#x1f4ca; 由 TrendForge 系统生成 | &#x1f310; https://trendforge.devlive.org/ &#x1f310; 本日报中的项目描述已自动翻译为中文 &#x1f4c8; 今日获星趋势图 今日获星趋势图2415markitdown570claude-code434ART330erpnext150MusicFree146rustdesk129vanna80…

Python+Tkinter制作音频格式转换器

我们将使用Python的Tkinter库来构建一个音频格式转换器界面。由于音频转换需要实际的处理&#xff0c;我们将使用pydub库&#xff08;需要安装&#xff09;来进行音频格式转换。同时&#xff0c;我们会使用ffmpeg作为后端&#xff0c;因此请确保系统中已安装ffmpeg并添加到环境…

Haproxy算法精简化理解及企业级高功能实战

文章目录4. Haproxy的算法4.1 静态算法4.1.1 static-rr&#xff1a;基于权重的轮询调度1. 示例&#xff1a;4.1.2 first1. 示例2. 测试效果&#xff1a;4.2 动态算法4.2.1 roundrobin1. 示例2. 动态调整权重4.2.2 leastconn1. 示例4.3 其他算法4.3.1 source1. 示例2. 测试4.3.2…

git fork的项目远端标准协作流程 仓库设置[设置成upstream]

这是一个在开源协作中非常常见的配置。 简单来说&#xff0c;upstream 在这里指的是你 Fork 来的那个原始的、官方的仓库。 下面我们来详细解释一下这个 git remote -v 输出的含义&#xff1a; 1. 两条“遥控器” (Remotes) 你的 git 配置了两个远程仓库的地址&#xff0c;就像…

[FFmpeg] 输入输出访问 | 管道系统 | AVIOContext 与 URLProtocol | 门面模式

链接&#xff1a;https://trac.ffmpeg.org/ docs&#xff1a;FFmpeg FFmpeg 是一个强大的多媒体框架&#xff0c;旨在处理媒体处理的各个阶段。 它就像一个数字媒体工厂&#xff0c;包含以下部门&#xff1a;打包/解包&#xff08;容器处理&#xff09;、 转译/压缩&#xff…

微服务的编程测评系统2

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录前言工程创建创建ck-oj创建oj-modules创建具体微服务oj-system推送码云管理员登录逻辑分析docker安装mysqldocker客户端docker desktop安装安装mysqlmysql-plus和数据…

AR智能巡检:电力运维的数字化变革

在电力行业快速发展的当下&#xff0c;传统运维方式已难以满足现代电网对高效、安全的需求。近年来&#xff0c;增强现实&#xff08;AR www.teamhelper.cn &#xff09;技术的兴起为电力巡检带来了全新的解决方案。通过实时数据可视化、远程协作和智能分析&#xff0c;AR技术…

NeRF和3DGS原理详细

NeRF和3DGS一、传统三维表征方法1.1 显示表征1.2 隐式表征二、NeRF&#xff08;Nerual Radiance Field&#xff09;2.1 NeRF场景表示2.2 NeRF训练流程2.3 NeRF体渲染2.4 NeRF位置编码2.5 NeRF体素分层采样&#xff08;Volume Hierarchical Sampling&#xff09;2.6 NeRF网络结构…

035_ClaudeCode_MCP_介绍

035_ClaudeCode_MCP_介绍 摘要 Model Context Protocol&#xff08;MCP&#xff09;是一个开放的标准化协议&#xff0c;专为大型语言模型提供上下文数据而设计。作为Claude Code生态系统的重要组成部分&#xff0c;MCP如同"AI应用程序的USB-C端口"&#xff0c;提供…

Python 程序无法找到 Oracle 的 64 位客户端库 (libclntsh.so)

数据库错误: DPI-1047: Cannot locate a 64-bit Oracle Client library: "libclntsh.so: cannot open shared object file: No such file or directory". See https://oracle.github.io/odpi/doc/installation.html#linux for help 这个错误表明 Python 程序无法找到…

Kubernetes常用命令总结

文章目录Kubernetes常用命令总结1. 集群管理命令kubectl cluster-infokubectl get nodeskubectl describe node <node-name>kubectl top nodes2. Pod相关命令kubectl get podskubectl get pods -o widekubectl describe pod <pod-name>kubectl logs <pod-name&g…