你用了五年的消息队列,不知道它背后站着中介者模式

📅 2026/8/12 14:25:16
你用了五年的消息队列,不知道它背后站着中介者模式
你用了五年的消息队列不知道它背后站着中介者模式先别急着翻 GoF 目录。我说一个你天天在用、但从没意识到它是设计模式的东西消息队列。不是比喻。消息队列就是中介者模式Mediator的分布式实现。Producer 和 Consumer 不直接通信所有消息经过 Broker 中转——这就是中介者模式的灵魂用一个中介对象封装一组对象的交互方式让它们不直接引用彼此。一个没有中介者的聊天室假设你写过一个简单的群聊功能。最开始你可能这么写java public class User { private String name; private List contacts; // 直接持有其他用户引用public void send(String message) { for (User u : contacts) { u.receive(this.name, message); } }} 功能没问题。现在需求来了有人退群怎么办要通知所有人刷新列表。有人被禁言只发消息给管理员。有人发敏感词先过一遍过滤规则再决定是否投递。上面的代码每加一个需求就要改 User 类——消息路由、过滤、黑名单全塞在一起。三个月后 User.send() 变成了 200 行的 if-else 地狱。更糟的是User 之间互相持有引用退群时要遍历所有 User 删引用漏一个就是内存泄漏。中介者模式怎么解决把通信逻辑抽出来放进一个独立的 ChatRoom 类。java public class ChatRoom { private Map users new HashMap();public void register(User user) { users.put(user.getName(), user); } public void send(String from, String to, String message) { User recipient users.get(to); if (recipient ! null !recipient.isMuted()) { recipient.receive(from, message); } } public void broadcast(String from, String message) { for (User u : users.values()) { if (!u.getName().equals(from) !u.isMuted()) { u.receive(from, message); } } }}public class User { private String name; private ChatRoom room; // 只持有中介者引用public void sendTo(String to, String message) { room.send(this.name, to, message); } public void sendAll(String message) { room.broadcast(this.name, message); }} User 从互相引用的网状结构变成只依赖 ChatRoom 的星型结构。加禁言功能只改 ChatRoomUser 一行不动。这就是中介者模式的核心价值把 M×N 的交互复杂度降为 MN。跟观察者模式的区别——很多人搞混中介者模式和观察者Observer模式都涉及对象间的通信但方向完全不同观察者Subject 不知道 Observer 是谁只管有事件发生了你们自己看着办。通信是单向广播。中介者通信双方都对 Mediator 发指令Mediator 决定怎么路由、怎么协调。通信是多向的、需要协调的。聊天室里A 发消息给 BB 的回复可能要通知 C这个过程中 ChatRoom 做决策。这就是中介者不是观察者。消息队列里的死信队列、延迟投递、消息过滤——这些都是 Mediator 的协调逻辑不是 Observer 能做到的。消息队列就是中介者模式的工业级实现回头看消息队列的架构Producer A ──→ ──→ Consumer X Producer B ──→ [Broker] ──→ Consumer Y Producer C ──→ ──→ Consumer Z这就是一个标准的 Mediator。Broker 居中协调生产者和消费者彼此不知道对方的存在。RocketMQ 里的 Tag 过滤、顺序消息、事务消息——这些都是 Mediator 在封装交互。如果没有 Broker 这个中介者你需要在每个 Producer 和每个 Consumer 之间建立直连连接数瞬间爆炸。我在一个订单系统里见过这种惨状。早期架构里订单服务直接调用库存、支付、物流三个服务——四个服务六对调用关系。后来加了优惠券和积分服务变成十五对调用关系。每改一个服务的接口要改四五个调用方。上了消息队列之后订单服务只管往 Topic 发消息。库存、支付、物流各自订阅感兴趣的 Topic。订单服务不需要知道消费者的存在甚至连消费者有几个都不关心。这不是架构演进这是中介者模式在工程层面的自然表达。Spring 里到处都是中介者的影子Spring 的 ApplicationEvent 机制本质上就是中介者模式java Component public class OrderMediator { Autowired private ApplicationEventPublisher publisher;public void placeOrder(Order order) { // 业务逻辑... publisher.publishEvent(new OrderPlacedEvent(order)); // ApplicationContext 作为中介者负责把事件路由给所有监听器 }}Component public class InventoryListener { EventListener public void handleOrderPlaced(OrderPlacedEvent event) { // 扣库存 —— 不需要知道谁发的只关心事件本身 } }Component public class NotificationListener { EventListener public void handleOrderPlaced(OrderPlacedEvent event) { // 发通知 —— 同上 } } ApplicationContext 就是那个隐形的 Mediator。你不需要在 OrderService 里注入 InventoryService 和 NotificationService你只管发事件。谁监听、怎么处理、有没有异常——这些协调逻辑被 Spring 容器封装了。DispatcherServlet 也是。它居中协调所有 HandlerMapping、HandlerAdapter、ViewResolverController 只需要关心自己的业务逻辑完全不知道 HTTP 请求是怎么被路由到自己这里的。三个真实的坑坑一上帝中介者。把太多协调逻辑塞进 Mediator它就变成了上帝对象——什么都管什么都依赖改一行影响全局。见过一个 ChatMediator 管了消息路由、敏感词过滤、用户权限、消息持久化、未读数统计、提醒、文件上传、表情解析。这个类是代码审查禁区——没人敢动。解法中介者也可以分层。ChatMediator 只做路由FilterChain 做过滤MessageStore 做持久化NotificationService 做提醒。每个 Mediator 有自己的职责边界。坑二不该中介的地方用了中介者。两个对象之间的简单调用不需要中介者。订单服务调用支付服务就一个调用方你引入消息队列变成异步——除非有削峰需求否则只是增加延迟和复杂度。判断标准当交互涉及三个以上对象或者交互逻辑频繁变化时才引入 Mediator。两个对象、逻辑稳定的场景直接调用比中间加一层 Broker 快得多。坑三消息队列不是银弹。有团队一说解耦就上消息队列结果一个 CRUD 系统里跑着五个 Topic排查问题时要在多个服务之间跳转。消息队列解了对象耦合但引入了时间耦合——你现在要关心消息丢失、重复消费、顺序性、积压告警。中介者模式的核心是把交互逻辑集中管理不是为了解耦而解耦。如果你的交互逻辑本身就不复杂直调可能是更好的选择。什么时候该用中介者模式最适合三种场景对象间的交互逻辑频繁变化。比如群聊里的禁言、黑名单、消息撤回——规则一直在变集中管比分散在 User 里好维护一百倍。需要复用交互组件但交互方式因场景不同。GUI 框架里同一个 Button 在对话框和表单里的行为不同——Mediator 换一下就行Button 本身不用改。减少子系统间的直接依赖。微服务里的消息队列就是典型案例。订单服务不需要知道库存服务的 API 签名只需要知道 Topic 名称。不一致的团队不要用。中介者模式把逻辑集中了但也把理解成本集中了。如果你团队里只有一个人懂那个 Mediator他一走项目就瘫痪。一句话总结中介者模式不是让你加个中间层是让你把对象间混乱的交互关系封装成一个可独立维护的协调单元。消息队列、Spring 事件、GUI 框架——都在做这件事只是没人叫它设计模式。我在做一个用卡皮巴拉讲设计模式的微信小程序「爪爪代码冒险记」23 个设计模式用漫画加答题的方式讲。中介者模式在里面的漫画场景是卡皮巴拉餐厅的传菜员——厨师和顾客不直接交流传菜员居中协调。感兴趣的话搜一下「爪爪代码冒险记」。