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里做耗时操作,否则拖慢所有发布方
什么时候该放弃“模式思维”,直接用结构体字段组合
很多所谓“中介需求”,其实只是两个结构体需要共享少量状态或触发简单动作——这时候最直白的方式就是把对方作为字段嵌入:
《PHP设计模式》首先介绍了设计模式,讲述了设计模式的使用及重要性,并且详细说明了应用设计模式的场合。接下来,本书通过代码示例介绍了许多设计模式。最后,本书通过全面深入的案例分析说明了如何使用设计模式来计划新的应用程序,如何采用PHP语言编写这些模式,以及如何使用书中介绍的设计模式修正和重构已有的代码块。作者采用专业的、便于使用的格式来介绍相关的概念,自学成才的编程人员与经过更多正规培训的编程人员
立即学习“go语言免费学习笔记(深入)”;
type ServiceA struct {
B *ServiceB // 显式依赖,非接口,非抽象
}
func (a *ServiceA) DoWork() {
a.B.Notify("from A")
}
type ServiceB struct {
logs []string
}
func (b *ServiceB) Notify(msg string) {
b.logs = append(b.logs, msg)
}
- 没有隐藏的间接层,调用链清晰,IDE 跳转直接,pprof 分析无盲区
- 只要
ServiceB不暴露内部细节(如不导出logs字段),就满足封装要求 - 如果未来真需要替换
ServiceB实现,再提取接口也不迟——Go 主张“先具体,后抽象”
真正容易被忽略的是:中介者模式的初衷是降低多对多依赖的复杂度,但在 Go 里,**过度提前抽象带来的维护成本,常常远高于几个显式字段或回调函数带来的耦合**。先写清楚逻辑,再看是否真的需要“中介”。









