Golang中介者模式如何降低模块耦合度_集中协调设计说明

Go语言中介者模式应避免类继承和接口强制实现,改用函数类型注册、channel异步事件总线及依赖注入组合,实现模块零import解耦。

Go 语言没有类继承和接口强制实现,直接照搬传统面向对象的中介者模式容易水土不服——关键不在于“写个 Mediator 结构体”,而在于用好 interface{}、函数值、通道和组合,让模块只依赖抽象协调逻辑,不互相 import。

用函数类型替代抽象中介者接口

在 Go 里硬套 Java 风格的 Mediator 接口(如 Notify(sender, event interface{}))会导致调用方仍需知道中介者存在。更轻量的做法是把协调逻辑定义为函数类型,由模块在初始化时注册:

type EventHandler func(event interface{})

var eventBus = make(map[string][]EventHandler)

func Register(topic string, handler EventHandler) { eventBus[topic] = append(eventBus[topic], handler) }

func Publish(topic string, event interface{}) { for _, h := range eventBus[topic] { h(event) } }

这样,OrderServiceInventoryService 只需调用 Publish("order.created", order),完全不知道谁在监听,也不需要导入对方包。

用 channel + goroutine 实现解耦的异步协调

同步回调仍有隐式依赖(比如 handler panic 会中断发布流程)。用通道做事件总线,天然支持异步、背压和错误隔离:

type Event struct {
    Topic string
    Data  interface{}
}

var eventCh = make(chan Event, 100)

func StartEventLoop() { go func() { for e := range eventCh { switch e.Topic { case "payment.succeeded": go notifyShipping(e.

Data) go updateAnalytics(e.Data) case "inventory.low": go triggerReorder(e.Data) } } }() }

func Emit(topic string, data interface{}) { select { case eventCh <- Event{Topic: topic, Data: data}: default: // 可选:丢弃或打日志,避免阻塞调用方 } }

  • Emit() 调用方完全不感知下游逻辑,也不依赖任何服务实例
  • 每个 handler 是独立 goroutine,一个 panic 不影响其他
  • 模块间零 import:支付模块发事件,物流模块自己 go notifyShipping,二者代码文件互不引用

组合 mediator 结构体时避免循环 import

如果确实需要一个集中管理的 Coordinator 结构体(比如要聚合状态、重试策略),切忌让各业务模块直接持有一个 *Coordinator 指针——这等于又绑死了依赖。正确做法是:

  • 定义纯行为接口:type Notifier interface { SendEmail(to, body string) error }
  • Coordinator 实现该接口,但业务模块只依赖 Notifier,不 import coordinator 包
  • 启动时用构造函数注入:orderSvc := NewOrderService(coordinator),而非在 order.go 里 import coordinator

这样,order.gocoordinator.go 之间没有 import 循环,编译期就切断了耦合。

真正难的不是写出中介者,而是克制住“我想控制一切”的冲动——Go 的解耦效果,往往取决于你愿意把多少协调逻辑推给 caller(函数参数)、多少交给 runtime(channel/goroutine)、多少留给启动时组合(依赖注入)。越早放弃“统一调度中心”的执念,模块就越松。