如何在Golang中实现观察者模式_Golang观察者模式事件通知示例

15次阅读

go观察者模式需手动实现,核心是用sync.RWMutex+slice安全管理订阅者,通知时复制列表并goroutine并发调用,接口应轻量明确,生命周期管理防泄漏。

如何在Golang中实现观察者模式_Golang观察者模式事件通知示例

Go 语言没有内置的事件系统或接口继承机制,观察者模式必须手动实现;核心在于用 mapslice 管理订阅者,并确保通知时避免并发写 panic 和重复注册/移除问题。

sync.Map 存储观察者避免竞态

多个 goroutine 同时调用 register / Unregister 时,普通 map 会 panic。虽然 sync.Map 不支持遍历,但可配合额外的 sync.RWMutex + slice 实现安全读写:

  • 只在注册/注销时加写锁,通知时用读锁遍历副本,性能更可控
  • 不要直接在通知循环中调用观察者方法——若某个观察者阻塞或 panic,会拖垮整个通知流
  • 推荐为每个观察者启动独立 goroutine(加 recover),但需注意资源泄漏风险

Observer 接口定义要轻量且明确

Go 中接口越小越好。观察者只需一个方法,参数应包含事件类型和有效载荷,不建议传入发布者实例(破坏解耦):

type Event string  const ( 	EventUserCreated Event = "user_created" 	EventOrderPaid   Event = "order_paid" )  type Observer Interface { 	OnEvent(event Event, data interface{}) }
  • data interface{} 提供灵活性,但调用方需保证类型一致,否则运行时报错
  • 避免定义 Update() 这类模糊方法名,无法体现事件语义
  • 如需区分事件子类型,可用结构体嵌套 Event 字段,而非靠接口方法重载

通知时复制观察者列表防止修改冲突

不能边遍历边修改切片,否则可能漏通知或 panic。典型做法是:读锁保护下拷贝当前列表,释放锁后再逐个通知:

立即学习go语言免费学习笔记(深入)”;

func (e *EventBus) Notify(event Event, data interface{}) { 	e.mu.RLock() 	observers := make([]Observer, len(e.observers)) 	copy(observers, e.observers) 	e.mu.RUnlock()  	for _, obs := range observers { 		go func(o Observer) { 			defer func() { 				if r := recover(); r != nil { 					log.Printf("observer panic: %v", r) 				} 			}() 			o.OnEvent(event, data) 		}(obs) 	} }
  • copy() 前必须已加读锁,否则可能拷贝到部分更新状态
  • 闭包中传参用 go func(o Observer) 而非直接 obs,避免循环变量复用问题
  • 如果业务要求强顺序或同步响应,去掉 go 并移除 recover,但需接受单点失败影响全局

真正难的不是注册和通知逻辑,而是生命周期管理:观察者何时该被自动清理?比如 http handler 注册了观察者,但 handler 返回后没人调用 Unregister,就会导致内存泄露。这种场景下,考虑用 context.Context 关联取消信号,或让观察者实现 io.Closer 接口显式退出。

text=ZqhQzanResources