
一. 引言
当我们第一次读到中介者模式(Mediator Pattern)时,可能会觉得有点抽象,有点啰嗦、脱离实际业务。
而我们这篇博客我打算不从教科书角度出发,而是从一个非常清晰的真实iOS业务场景中,一步一步来演示中介者模式在实际开发中的使用,以及它所解决的问题。
中介者模式:用一个对象来封装一些列对象的交互方式,中介者使各个对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
二. 业务场景描述
我们就以一个十分场景的业务场景为例,一个视频播放器的播放页,播放页的页面功能通常会比较复杂,除了播放功能外往往还需要:
- 处理字幕信息。
- 处理弹幕信息。
- 处理拖拽及点击手势。
- 进行埋点统计。
等等。
我们就提出来4个功能,这4个功能呢分布于不同的四个类当中:
- PlayerView:负责视频的播放功能。
- ControlBarView:负责视频控制功能,比如播放、暂停、拖拽进度、拖拽亮度、音量等等。
- SubtitleView:负责处理字幕功能。
- AnalyticsManager:负责埋点统计。
一个非常简单的需求场景,当用户点击播放按钮时:
- PlayerView 开始播放视频。
- ControlBarView 按钮状态变为播放中。
- SubtitleView 字幕开始更新。
- AnalyticsManager 上报一次播放埋点。
这听起来确实很容易,我们甚至不需要考虑就把代码写出来。
三. 不使用中介者的写法
即使不使用中介者,我们也可以实现这样的需求。
比如ControlBarView 控制视图内的实现:
Swift
class ControlBarView {
// 播放视图
var playerView: PlayerView?
// 字幕视图
var subtitleView: SubtitleView?
// 埋点管理类
var analytics: AnalyticsManager?
/// 播放按钮点击
func playButtonTapped() {
playerView?.play()
subtitleView?.start()
analytics?.track(event: "play")
}
}
这表面上没有什么问题,但是实际上:
- ControlBarView 知道太多的对象了。
- 所有的联动逻辑都写死在了View里面。
- 新增或者删除一个响应方,都需要调整这个View的代码。
UI现在承担了一个业务调度者的角色,这是非常不妥当的一个行为。
就目前的实现而言:
- UI组件之间强耦合。
- 交互逻辑分散在各个View当中。
- 后续需求一旦开始复杂将会需要越来越多的代码和逻辑,会变得开始失控。
那么 中介者 要解决的就是这个问题。
四. 引入中介者(PlaybackMediator)
接下来我们开始引入中介者,为了更方便理解,我们尽量将内容简单化。
4.1 定义中介者协议
我们采用协议的形式来定义作为中介者,需要实现的方法:
Swift
protocol PlaybackMediator: AnyObject {
func playTriggered()
}
4.2 中介者的具体实现
接下来我们需要定义一个中介者类,并循序中介者协议。与此同时该对象内还应该引用到所有同事对象。我们把前面几个相互协作的对象就成为同事对象。
Swift
class PlaybackCenter: PlaybackMediator {
// 播放视图
let playerView: PlayerView
// 控制视图
let controlBar: ControlBarView
// 字幕视图
let subtitleView: SubtitleView
// 埋点管理
let analytics: AnalyticsManager
init(playerView: PlayerView,
controlBar: ControlBarView,
subtitleView: SubtitleView,
analytics: AnalyticsManager) {
self.playerView = playerView
self.controlBar = controlBar
self.subtitleView = subtitleView
self.analytics = analytics
}
// 播放方法
func playTriggered() {
playerView.play()
controlBar.updatePlayingState()
subtitleView.start()
analytics.track(event: "play")
}
}
所有的联动都将集中到这个类当中。
4.3 同事对象只和中介者通讯
而同事对象之间将不再进行通讯,而是直接与中介者进行通讯。
ControlBarView
Swift
class ControlBarView {
weak var mediator: PlaybackMediator?
func playButtonTapped() {
mediator?.playTriggered()
}
func updatePlayingState() {
print("UI 更新为播放中")
}
}
PlayerView
Swift
class PlayerView {
func play() {
print("开始播放视频")
}
}
SubtitleView
Swift
class SubtitleView {
func start() {
print("字幕开始")
}
}
五. 结构变化分析
在我们没有使用中介之间呢我们的结构是通过 ControlBarView 来连接 playerView、subtitleView和 analytics。
而引入中介之后呢UI组件之间不再有任何关联,ControlBarView 链接 PlaybackMediator, 而PlaybackMediator链接playerView、subtitleView和analytics。
就实现了 组件之间零耦合、UI 只负责 UI、业务联动集中管理。
六. 结语
在 iOS 的世界里,中介者其实一点也不陌生。UITableViewDelegate、UICollectionViewDelegate、Coordinator / Router,以及 MVVM-C 中的协调层,它们都有一个非常一致的特征:
页面或组件本身,不负责跳转、不负责调度,只负责"汇报事件"。
真正决定"接下来该谁做什么"的,是那个站在中间的角色。也正因为如此,当你在项目中逐渐遇到这些信号时------一个类开始同时持有多个对象,View 或 ViewModel 里出现明显的业务流程判断,改一个交互却需要同时修改好几个文件,其实并不是你代码写得不够好,而是复杂度已经在提醒你:需要一个中介者了。
当然,中介者模式也并非没有代价。最常见、也最危险的坑,就是它悄悄膨胀成一个 God Object:一个 Mediator 几百行代码,if / switch 满天飞,所有事件都往里塞,最终变成新的"混乱中心"。
更合理的做法是:按业务场景拆分中介者,一个中介者只负责一个清晰、可描述的协作流程,不要害怕多几个 Mediator 类------它们恰恰是在帮你控制复杂度,而不是制造复杂度。
所以,中介者模式并不是为了"用设计模式而用设计模式"。而是当你发现:
- 一个类知道得太多
- 一个改动牵一堆地方
- View 里开始写本不属于它的业务判断
那你其实已经站在了需要中介者模式的边缘。
感谢大家阅读。