Mediator接口设计需避免循环依赖、确保同事安全持有非nil引用、用事件名解耦通知逻辑、并发下Notify需线程安全。应将接口独立定义,同事构造强制传入Mediator,禁用匿名嵌入,注册/注销加锁,状态更新用atomic,Notify中避免耗时操作。

Mediator 接口设计要避开循环依赖
Go 没有类继承,靠接口和组合实现松耦合,但中介者模式里容易让 Mediator 依赖具体同事(Colleague),而同事又反向持有 Mediator,一不小心就循环 import。这不是语法错误,而是构建失败或测试时包初始化死锁。
- 把
Mediator接口定义在独立的mediator.go文件里,不引入任何同事类型 - 同事结构体只依赖
Mediator接口,不依赖具体实现;实现类放在另一个包(如concrete/)里,按需导入同事包 - 用函数选项或工厂函数注入中介者,避免在同事构造时硬编码
newConcreteMediator()
同事对象如何安全持有 Mediator 引用
同事结构体字段存 Mediator 接口没问题,但必须确保它非 nil,否则调用 Notify() 会 panic。常见错误是忘记初始化,或在嵌入结构体时误以为父字段自动赋值。
- 同事的构造函数(如
NewButton())强制接收m Mediator参数,不提供无参版本 - 避免用匿名嵌入 + 默认零值字段:像
type Button struct { Mediator }是危险的,应显式声明mediator Mediator - 可在
Notify()内部加if m.mediator == nil { return }防崩,但不如编译期约束可靠
type Button struct {
mediator Mediator
name string
}
func NewButton(name string, m Mediator) *Button {
return &Button{mediator: m, name: name}
}
通知逻辑里别直接调用同事方法
中介者的核心价值是解耦,但如果 ConcreteMediator.HandleNotify() 里写满 b1.Click(); b2.Disable(),其实只是把耦合从同事挪到了中介者内部——同事还是得知道彼此存在,且新增同事就得改中介者代码。
- 用事件名(
string)或枚举(EventType)作为通知载荷,同事自行判断是否响应 - 中介者只做广播或路由,不决定“谁该做什么”,例如:
m.Notify("user_logged_in", data) - 同事注册回调时传入闭包或方法值,而不是让中介者硬编码调用链
并发场景下 Notify 调用不是线程安全的
Go 中多个 goroutine 同时触发 Notify() 很常见,但默认的中介者实现若用 map 存同事列表、或共享状态(如计数器、开关),极易出现 data race。
立即学习“go语言免费学习笔记(深入)”;
- 同事注册/注销操作必须加
sync.RWMutex,尤其Register()和Unregister() - 如果中介者需要维护状态(比如当前活跃用户数),用
sync/atomic替代普通 int - 避免在
Notify()中执行耗时操作(如 HTTP 请求、DB 查询)——这会让其他同事阻塞,应转为异步处理
真正难的不是写出四五个结构体,而是让中介者既不成为性能瓶颈,也不在并发下悄悄错乱。多数人卡在第 3 步之后,忘了通知机制本身也需要被保护。










