Go (Golang) 中的事件总线与观察者模式
在现代 Go (Golang) 开发中,经常需要组织应用程序组件之间的松散耦合交互。两种经典方法是 观察者模式 及其更高级的实现——事件总线。这些机制允许程序的一部分对其他部分的行为做出响应,而无需创建硬性依赖。
在本文中,我们将探讨如何在不依赖外部库的情况下,用纯 Go 实现事件总线,研究观察者模式,并展示同步和异步事件处理的代码示例。
什么是观察者模式?
观察者模式是一种行为设计模式,它创建了一种订阅机制,允许一个对象观察并响应另一个对象中发生的事件。
主要元素:
- 主题 — 生成事件的对象。
- 观察者 — 订阅事件的对象。
- 事件 — 从主题传递给观察者的数据。
什么是事件总线?
事件总线是一个集中式总线,用于管理发布者和订阅者之间的事件传递。与经典的观察者模式不同(其中主题直接存储观察者列表),事件总线充当中介。这使得系统更加灵活和可扩展。
在 Go 中实现事件总线
Go 没有内置的事件总线实现,但可以使用 goroutine、通道和接口轻松编写。让我们看一个简单但功能完整的实现。
1. 定义基本类型
// Event 表示事件结构type Event struct { Name string Data interface{}}
// EventHandler — 事件处理函数type EventHandler func(Event)2. 创建事件总线
type EventBus struct { handlers map[string][]EventHandler mu sync.RWMutex}
// NewEventBus 创建一个新的事件总线func NewEventBus() *EventBus { return &EventBus{ handlers: make(map[string][]EventHandler), }}
// Subscribe 将处理程序订阅到事件func (eb *EventBus) Subscribe(name string, handler EventHandler) { eb.mu.Lock() defer eb.mu.Unlock() eb.handlers[name] = append(eb.handlers[name], handler)}
// Publish 同步发布事件func (eb *EventBus) Publish(name string, data interface{}) { eb.mu.RLock() defer eb.mu.RUnlock() if handlers, ok := eb.handlers[name]; ok { event := Event{Name: name, Data: data} for _, handler := range handlers { handler(event) } }}3. 异步发布
对于异步处理,可以在单独的 goroutine 中运行每个处理程序:
// PublishAsync 异步发布事件func (eb *EventBus) PublishAsync(name string, data interface{}) { eb.mu.RLock() defer eb.mu.RUnlock() if handlers, ok := eb.handlers[name]; ok { event := Event{Name: name, Data: data} for _, handler := range handlers { go handler(event) } }}使用示例
让我们看看如何在真实应用程序中使用事件总线。假设我们有一个记录用户操作并发送通知的系统。
func main() { bus := NewEventBus()
// 订阅 "user.login" 事件 bus.Subscribe("user.login", func(e Event) { userData := e.Data.(map[string]string) fmt.Printf("日志:用户 %s 已登录系统", userData["username"]) })
// 订阅同一事件以用于