Java 23 种设计模式:从踩坑到精通 | 中介者模式 —— 对象关系太乱?请一位“中间人”
Java 23 种设计模式:从踩坑到精通 | Mediator —— 对象关系太乱?请一位“中间人”
摘要:当多个对象之间形成复杂的网状交互时,直接相互引用会导致“牵一发而动全身”的维护灾难。中介者模式通过引入一个中间协调对象,将所有交互逻辑集中管控,让原本互相引用的对象只与中介者通信,从而将“多对多”转为“一对多”。本文从智能家居设备联动的场景出发,完整讲解中介者模式的原理、UML、代码实现,与外观模式进行深度对比,并结合 Spring MVC
DispatcherServlet、消息中间件、微服务注册中心等框架应用,帮你掌握“化网为星”的设计精髓。
🗺️本文阅读地图(3 分钟速览)
- 为什么设备之间互相引用会爆炸?
- 中介者模式核心角色拆解
- 手写智能家居中控(电视、窗帘、灯光联动)
- 聊天室案例:广播消息一目了然
- Spring MVC
DispatcherServlet/ 消息中间件 / 注册中心如何体现- 面试必问:中介者 vs 外观,到底怎么区分?
📖《Java 23 种设计模式:从踩坑到精通》
开篇:系列介绍与目录 | 上一篇:迭代器模式 |当前:中介者模式| 下一篇:备忘录模式
🔗 返回系列总目录
文章目录
- Java 23 种设计模式:从踩坑到精通 | Mediator —— 对象关系太乱?请一位“中间人”
- 1. 从一个“设备联动”的交互噩梦说起
- 1.1 你的场景该不该用中介者?
- 2. 模式定义与 UML 结构
- 图文解析(配合上述 UML 图)
- 3. 代码实现:智能家居中控
- 3.1 抽象中介者
- 3.2 抽象同事类
- 3.3 具体同事类:电视、窗帘、灯光
- 3.4 具体中介者:智能中控
- 3.5 客户端
- 4. 代码实现:聊天室(经典面试场景)
- 4.1 中介者
- 4.2 具体中介者
- 4.3 同事类:用户
- 4.4 客户端
- 5. 优缺点一览
- 6. 中介者模式 vs 外观模式
- 7. 框架与实践中的应用
- 7.1 Spring MVC:`DispatcherServlet`
- 7.2 消息中间件(RabbitMQ、Kafka)
- 7.3 微服务注册中心(Eureka、Nacos)
- 8. 面试必问 + 面试官追问连环炮
- 9. 六大设计原则在中介者模式中的体现
- 🧭 《Java 23 种设计模式:从踩坑到精通》快速导航
1. 从一个“设备联动”的交互噩梦说起
假设你正在开发一套智能家居系统,有窗帘、灯光、电视、空调等多个设备,它们之间存在复杂的联动规则:
- 打开电视时,窗帘自动关闭,灯光调暗;
- 打开空调时,窗户自动关闭;
- 按下“离家模式”时,所有设备全部关闭;
- ……
如果不加设计,每个设备对象都要持有其他设备的引用:
classTV{privateCurtaincurtain;privateLightlight;voidon(){curtain.close();light.dim();}}classAirConditioner{privateWindowwindow;voidon(){window.close();}}这种网状结构的耦合是灾难性的:
- 新增一个设备(如加湿器),所有相关设备都要修改;
- 修改一条联动规则,可能要改动多个设备类;
- 设备之间互相依赖,单元测试几乎无法独立进行。
中介者模式(Mediator Pattern)正是为解决“多对多”对象交互问题而生的:它引入一个中介者对象,封装所有对象之间的交互逻辑,让每个对象只与中介者通信,从而将网状结构变为星型结构。
1.1 你的场景该不该用中介者?
| 判断标准 | 是 → 用中介者 | 否 → 用其他方式 |
|---|---|---|
| 多个对象之间形成复杂的网状依赖关系 | ✅ | ❌ |
| 交互逻辑频繁变化,需要集中管理 | ✅ | ❌ |
| 希望各组件独立开发、独立测试 | ✅ | ❌ |
| 只有简单的一对一调用关系 | ❌ | 直接调用即可 |
2. 模式定义与 UML 结构
中介者模式用一个中介对象来封装一系列对象的交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。它属于行为型设计模式。
图文解析(配合上述 UML 图)
中介者模式的核心角色:
- 抽象中介者(
Mediator):定义同事对象到中介者的通信接口,通常只有一个notify(sender, event)方法。 - 具体中介者(
ConcreteMediator):实现中介者接口,管理所有同事对象,协调它们之间的交互逻辑。这是整个模式的核心。 - 抽象同事类(
Colleague):持有中介者引用,发送消息时通过中介者转发,自己不与其他同事直接通信。 - 具体同事类(
ConcreteColleagueA/ConcreteColleagueB):实现自身业务逻辑,通过中介者与其他同事交互。
核心机制:同事类从“互相认识”变为“只认识中介者”。发送消息时调用mediator.notify(this, event),由中介者决定通知哪些同事。这就是从“网状结构”到“星型结构”的关键。
3. 代码实现:智能家居中控
3.1 抽象中介者
publicinterfaceMediator{voidnotify(Componentsender,Stringevent);}💬白话:中介者只需一个方法——谁发了什么事,我来决定告诉谁。
3.2 抽象同事类
publicabstractclassComponent{protectedMediatormediator;publicComponent(Mediatormediator){this.mediator=mediator;}publicvoidsend(Stringevent){mediator.notify(this,event);}publicabstractvoidreceive(Stringevent);}💬白话:每个设备都认识中介者,发消息走中介者,收消息靠自己。
3.3 具体同事类:电视、窗帘、灯光
publicclassTVextendsComponent{publicTV(Mediatormediator){super(mediator);}publicvoidturnOn(){System.out.println("【电视】打开");send("tv_on");}@Overridepublicvoidreceive(Stringevent){if("curtain_closed".equals(event)){System.out.println("【电视】检测到窗帘已关闭,调整画面亮度");}}}publicclassCurtainextendsComponent{publicCurtain(Mediatormediator){super(mediator);}publicvoidclose(){System.out.println("【窗帘】关闭");send("curtain_closed");}@Overridepublicvoidreceive(Stringevent){if("tv_on".equals(event)){this.close();}}}publicclassLightextendsComponent{publicLight(Mediatormediator){super(mediator);}publicvoiddim(){System.out.println("【灯光】调暗");}publicvoidoff(){System.out.println("【灯光】关闭");}@Overridepublicvoidreceive(Stringevent){if("tv_on".equals(event)){this.dim();}}}💬白话:电视只管自己打开并发出“我开了”的消息,至于窗帘和灯光要做什么,电视完全不知道——这些逻辑全在中介者里。
3.4 具体中介者:智能中控
publicclassSmartHomeMediatorimplementsMediator{privateTVtv;privateCurtaincurtain;privateLightlight;publicvoidsetTv(TVtv){this.tv=tv;}publicvoidsetCurtain(Curtaincurtain){this.curtain=curtain;}publicvoidsetLight(Lightlight){this.light=light;}@Overridepublicvoidnotify(Componentsender,Stringevent){switch(event){case"tv_on":// 电视打开 → 窗帘关闭,灯光调暗curtain.receive("tv_on");light.receive("tv_on");break;case"curtain_closed":// 窗帘关闭 → 电视收到通知tv.receive("curtain_closed");break;}}}💬白话:中介者是“大脑”,所有联动规则集中在这里。电视打开时顺便关窗帘、调灯光——这条规则只写一次,在中介者里。
3.5 客户端
SmartHomeMediatormediator=newSmartHomeMediator();TVtv=newTV(mediator);Curtaincurtain=newCurtain(mediator);Lightlight=newLight(mediator);mediator.setTv(tv);mediator.setCurtain(curtain);mediator.setLight(light);// 用户打开电视tv.turnOn();// 输出:// 【电视】打开// 【窗帘】关闭// 【灯光】调暗// 【电视】检测到窗帘已关闭,调整画面亮度所有联动规则全部集中在SmartHomeMediator.notify()中,新增设备或修改规则只需改动中介者类,同事类无需修改。
4. 代码实现:聊天室(经典面试场景)
聊天室是中介者模式的另一个经典例子。用户不直接发消息给另一个用户,而是发送给聊天室,由聊天室广播给所有用户。
4.1 中介者
publicinterfaceChatRoom{voidsendMessage(Usersender,Stringmessage);voidaddUser(Useruser);}4.2 具体中介者
publicclassChatRoomImplimplementsChatRoom{privateList<User>users=newArrayList<>();@OverridepublicvoidaddUser(Useruser){users.add(user);}@OverridepublicvoidsendMessage(Usersender,Stringmessage){for(Useruser:users){if(user!=sender){user.receive(sender.getName(),message);}}}}💬白话:聊天室就是中介者,收到一条消息后广播给所有人(除了发送者自己)。
4.3 同事类:用户
publicclassUser{privateStringname;privateChatRoomroom;publicUser(Stringname,ChatRoomroom){this.name=name;this.room=room;room.addUser(this);}publicStringgetName(){returnname;}publicvoidsend(Stringmessage){System.out.println(name+" 发送: "+message);room.sendMessage(this,message);}publicvoidreceive(Stringfrom,Stringmessage){System.out.println(name+" 收到来自 "+from+" 的消息: "+message);}}💬白话:用户只知道聊天室,不知道其他用户的存在。发消息走聊天室,收消息也是聊天室送来的。
4.4 客户端
ChatRoomroom=newChatRoomImpl();Useralice=newUser("Alice",room);Userbob=newUser("Bob",room);Usercharlie=newUser("Charlie",room);alice.send("大家好!");// Alice 发送: 大家好!// Bob 收到来自 Alice 的消息: 大家好!// Charlie 收到来自 Alice 的消息: 大家好!5. 优缺点一览
| 优点 | 缺点 |
|---|---|
| 解耦:同事类之间零依赖,从网状变星型 | 中介者可能膨胀:所有交互逻辑集中在中介者中,可能成为“上帝类” |
| 集中控制:交互逻辑统一管控,修改方便 | 中介者复杂度上升:随着同事类增加,维护成本快速上升 |
| 简化测试:同事类可脱离具体中介者独立测试 | 性能瓶颈:所有通信都经过中介者,高并发场景可能成为瓶颈 |
| 符合单一职责:同事类只负责自身业务,交互逻辑交给中介者 |
6. 中介者模式 vs 外观模式
这两个模式都充当“中间人”角色,但目的完全不同:
| 对比维度 | 中介者模式 | 外观模式 |
|---|---|---|
| 目的 | 协调多个对象之间的双向交互 | 为子系统提供单向统一入口 |
| 通信方向 | 多对多 → 一对多(同事 ↔ 中介者 ↔ 同事) | 单向调用(客户端 → 外观 → 子系统) |
| 关注点 | 对象之间如何协作 | 简化客户端调用 |
| 子系统感知 | 同事类知道中介者的存在 | 子系统不知道外观存在 |
| 典型应用 | 聊天室、设备联动、MVC 的 Controller | SLF4J、JdbcTemplate、微服务网关 |
💡 外观是“统一入口”,中介者是“通信枢纽”。外观不知道客户端的存在,中介者知道所有同事。
7. 框架与实践中的应用
7.1 Spring MVC:DispatcherServlet
DispatcherServlet是 Spring MVC 的核心,它充当了 Controller、ViewResolver、HandlerMapping等众多组件之间的中介者。各组件不直接交互,而是通过DispatcherServlet协调请求分派、视图解析、异常处理等流程。
7.2 消息中间件(RabbitMQ、Kafka)
消息中间件的 Broker 就是典型的中介者。生产者与消费者不直接连接,而是通过 Broker 中转消息,Broker 负责路由、广播、持久化等交互逻辑。
7.3 微服务注册中心(Eureka、Nacos)
在微服务架构中,服务之间不直接写死地址,而是通过注册中心发现彼此。注册中心就是中介者,服务只与注册中心通信,获取其他服务实例的信息。
8. 面试必问 + 面试官追问连环炮
基础必问
- 中介者模式的核心角色?→
Mediator(中介者)、Colleague(同事类)。 - 中介者模式如何解耦?→ 将对象间的多对多依赖转为一对多依赖,同事类只依赖中介者。
- Spring MVC 中的
DispatcherServlet是什么模式?→ 中介者模式,协调所有 MVC 组件。
面试官追问
- “中介者和外观有什么区别?”
👉 外观是单向简化调用入口,中介者是双向协调交互。外观不知道客户端的存在,中介者知道所有同事。 - “中介者膨胀了怎么办?”
👉 引入多个中介者分层协作,按业务域拆分。如智能家居可以拆分为“影音中控”、“环境中控”等。 - “消息队列是中介者模式吗?”
👉 是。生产者与消费者不直接通信,通过 Broker 中转,Broker 就是中介者。
🎉恭喜:如果你能立刻说出
DispatcherServlet是中介者模式,消息队列 Broker 也是中介者思想,并清楚中介者与外观的本质区别,你已经掌握了行为型模式中“化网为星”的设计精髓。
9. 六大设计原则在中介者模式中的体现
| 设计原则 | 在中介者模式中的体现 |
|---|---|
| 单一职责原则(SRP) | 同事类负责自身业务,中介者负责交互协调,职责分离 |
| 开闭原则(OCP) | 新增同事类通常无需修改中介者;新增交互规则只需修改中介者 |
| 里氏替换原则(LSP) | 同事类都遵循抽象Colleague接口,可替换 |
| 依赖倒置原则(DIP) | 同事类依赖抽象Mediator接口,不依赖具体中介者 |
| 接口隔离原则(ISP) | Mediator接口定义精简,仅暴露notify() |
| 迪米特法则(LoD) | 同事类只与中介者通信,完全不知道其他同事的存在 |
🧭 《Java 23 种设计模式:从踩坑到精通》快速导航
- 开篇:系列介绍与目录
- 上一篇:迭代器模式 —— 遍历集合,为什么不直接暴露内部结构?
- 当前:中介者模式 —— 对象关系太乱?请一位“中间人”(你在这里)
- 下一篇:备忘录模式 —— 快照与撤销,给对象装一个“后悔药” 🚧 即将发布
- 创建型模式汇总:单例、工厂、建造者、原型
- 结构型模式汇总:适配器、装饰器、代理……
- 行为型模式汇总:观察者、策略、模板方法……
🔔 关注《Java 23 种设计模式:从踩坑到精通》,用 25 篇文章彻底吃透设计模式。
📦福利预告:全系列代码及 UML 源码将在完结时统一打包开放,点击「关注」「收藏」第一时间获取。
🚀下一篇:备忘录模式:快照与撤销,给对象装一个“后悔药”!🚧 即将发布,敬请关注!
📌 除了设计模式,我也在深挖智能物流实战(WMS、托盘调度、机器学习落地)。欢迎点击头像,看看专栏 《出版社物流WMS智能调度实战》。技术相通,思路可鉴。
