中介者模式:让对象间的沟通不再混乱

说实话,我刚开始做架构设计那几年,最头疼的就是对象之间的通信问题。你想想看,一个系统里十几个类互相调用,A调B,B调C,C又调A……代码写到最后,自己都理不清谁依赖谁。后来我接触到了中介者模式,才明白——原来很多混乱,都是因为缺少一个「中间人」。

中介者模式,说白了就是给一群对象找个「管家」。所有对象不再直接互相通信,而是通过这个管家来协调。这样,对象之间就解耦了,各自只关心自己的事。

中介者模式的结构

这个模式的结构其实很清晰,我画了张图帮你理解:

中介者 (Mediator) 同事A (Colleague) 同事B (Colleague) 同事C (Colleague) ✗ 不直接通信 ✗ 不直接通信 所有同事类只与中介者交互 中介者封装了对象间的交互逻辑 // 同事类只持有中介者引用 class Colleague { mediator.notify(this, event); }

这个模式里有几个关键角色:

  • 中介者接口:定义了通信的方法,比如 notify、send 之类的。
  • 具体中介者:实现协调逻辑,知道每个同事类该做什么。
  • 同事类:就是那些原本要互相调用的对象,现在只依赖中介者。

我在项目中遇到过这样一个场景:一个聊天室系统,用户、群组、消息管理三个模块互相调用。用户发消息要通知群组,群组要更新消息列表,消息管理要记录日志……代码耦合得一塌糊涂。后来我用中介者模式,引入了一个 ChatMediator,所有模块只跟它通信,瞬间清爽了。

中介者模式 vs 观察者模式

这两个模式经常被拿来比较,我刚开始学的时候也容易搞混。其实它们的核心区别在于——通信的方向和目的不同

对比维度 中介者模式 观察者模式
通信方向 多对多 → 集中到中介者 一对多(一个主题,多个观察者)
目的 解耦多个对象之间的交互 状态变化时通知依赖方
控制权 中介者主动协调 观察者被动接收通知
典型场景 对话框组件、聊天室、工作流 事件监听、数据绑定、消息队列

举个例子你就明白了。观察者模式就像你订阅了一个公众号——公众号发文章,你被动收到通知。而中介者模式更像一个会议室——所有人都在会议室里说话,会议室(中介者)负责把话传给该听的人。

我的经验之谈: 如果系统里是「一个对象变化,多个对象需要响应」,用观察者模式。如果是「多个对象之间互相调用,关系复杂」,用中介者模式。我曾经在一个项目中把观察者模式硬套在多对多场景上,结果回调嵌套了四五层,调试到崩溃……后来改成中介者,世界清净了。

中介者模式在 MVC 中的 Controller 角色

说到中介者模式,就不得不提 MVC 架构。你想想看,MVC 里的 Controller 在干什么?

  • 用户点击按钮 → View 通知 Controller
  • Controller 更新 Model
  • Model 变化 → Controller 通知 View 刷新

这不就是中介者吗?View 和 Model 不直接通信,全由 Controller 来协调。我习惯把 Controller 看作「胶水代码」——它不负责业务逻辑,只负责把 View 的事件转成 Model 的操作,再把 Model 的变化转成 View 的更新。

来看一段简单的代码示例:

// 中介者 —— Controller
class UserController {
    constructor(userModel, userView) {
        this.model = userModel;
        this.view = userView;
        
        // View 通知 Controller
        this.view.onSaveButtonClick((data) => {
            this.handleSaveUser(data);
        });
    }
    
    handleSaveUser(data) {
        // Controller 协调 Model
        this.model.save(data);
        
        // Controller 协调 View
        this.view.showSuccessMessage();
        this.view.refreshList(this.model.getAll());
    }
}

// View 和 Model 之间没有直接依赖
class UserView {
    onSaveButtonClick(callback) {
        // 绑定按钮点击事件
    }
}

class UserModel {
    save(data) { /* 保存逻辑 */ }
    getAll() { /* 查询逻辑 */ }
}
小提示: 写 Controller 的时候,记得保持它「薄」一点。我见过有人把业务逻辑全塞进 Controller,结果 Controller 几千行,比 Model 和 View 加起来还大。记住,Controller 只做协调,不做业务。

不过这里有个坑——Controller 如果太「厚」,就会变成上帝对象。我曾经接手过一个老项目,Controller 里既有数据校验、又有权限判断、还有日志记录……改一个功能要动十几个方法。后来我重构时,把校验逻辑放到了 Model 层,权限判断放到了中间件,Controller 只保留路由和协调,代码量直接砍了一半。

避坑指南: 我曾经犯过一个错误——让 Controller 直接操作 DOM。结果 View 的渲染逻辑和 Controller 的协调逻辑混在一起,改 UI 样式时还要担心会不会影响业务逻辑。后来我强制自己:Controller 只调用 View 的方法,绝不直接操作 DOM。这个习惯一直保持到现在。

总结一下中介者模式的核心价值:它把「网状结构」变成了「星型结构」。每个对象只跟中介者打交道,降低了耦合度,也让系统的扩展性更好。你想想看,如果以后要加一个新同事类,只需要让它跟中介者通信就行,其他类完全不用改——这就是开闭原则的体现。

我个人习惯在系统设计初期,先画出对象之间的通信图。如果发现连线太多、关系复杂,就会考虑引入中介者模式。这个习惯帮我避免了很多后期重构的麻烦。


公众号:蓝海资料掘金营,微信deep3321