Go语言很少直接写传统中介者模式,因其无类继承和接口强制约束,硬套UML结构反而加重代码、降低可测性;真实项目多用组合+函数回调+通道解耦,如用func类型替代Mediator接口、chan实现事件分发,或直接字段嵌入,遵循“先具体后抽象”原则。
为什么 Go 语言里很少直接写传统中介者模式
Go 没有类继承、没有接口实现强制约束,也没有“对象引用传递”意义上的中心化对象注册表;硬套 UML 类图里的 Mediator 抽象类 + 多个 Colleague 子类结构,反而会让代码变重、难测、易耦合。真实项目中更常见的是用组合+函数回调+通道来解耦协作逻辑,而不是造一个“中介者实例”来管理所有同事。
用 func 类型和闭包替代 Mediator 接口
把中介逻辑抽象成可注入的函数,比定义一堆接口和实现更符合 Go 的惯用法。比如两个模块需要协同更新状态,与其让它们都依赖某个 Mediator 实例,不如由上层统一传入回调:
// 定义协作行为:当 A 发生变更时,通知 B 做什么 type OnAUpdate func(aValue string)// A 模块只关心自己能触发什么,不关心谁响应 type ModuleA struct { onUpdate OnAUpdate }
func (a *ModuleA) TriggerChange(val string) { if a.onUpdate != nil { a.onUpdate(val) } }
// B 模块接收通知并执行本地逻辑 type ModuleB struct { lastSeen string }
func (b *ModuleB) HandleAUpdate(val string) { b.lastSeen = val }
- 避免了全局单例或构造时强依赖
Mediator实例 -
OnAUpdate可以是普通函数、方法值、甚至带状态的闭包 - 单元测试时可轻松 mock 行为,无需模拟整个中介者生命周期
用 chan 实现松耦合事件分发(适合跨 goroutine 场景)
当协作涉及异步、并发或跨组件边界(如 HTTP handler 和后台 worker),用通道比函数回调更自然:
// 中介者退化为一个事件总线:只负责转发,不持有业务逻辑
type EventBroker struct {
events chan Event
}
type Event struct {
Type string
Data interface{}
}
func NewEventBroker() *EventBroker {
return &EventBroker{
events: make(chan Event, 100), // 缓冲防阻塞
}
}
// 各模块只订阅自己关心的 Type,不互相 import
func (e *EventBroker) Publish(evt Event) {
select {
case e.events <- evt:
default:
// 队列满时丢弃或打日志,不 panic
}
}
- 发布者完全不知道谁消费,消费者也无需知道谁发布
- 注意缓冲区大小和背压处理,否则
chan满会导致协程卡死 - 别在
Publish里做耗时操作,否则拖慢所有发布方
什么时候该放弃“模式思维”,直接用结构体字段组合
很多所谓“中介需求”,其实只是两个结构体需要共享少量状态或触发简单动作——这时候最直白的方式就是把对方作为字段嵌入: