Skip to content

Observer

观察者模式(Observer Pattern)是一种行为型设计模式,它允许一个对象(主题/被观察者)在其状态发生变化时,自动通知所有依赖于它的对象(观察者)。该模式用于实现一对多的依赖关系,特别适合用来解耦事件的发送者和接收者。

在 Go 中实现观察者模式,可以通过以下方式来实现:

  1. 主题(Subject):持有观察者列表,并且提供注册、移除观察者的方法。
  2. 观察者(Observer):定义一个更新方法,当主题状态发生变化时,通知所有观察者。
  3. 事件通知:当主题的状态发生变化时,通知所有注册的观察者进行处理。

观察者模式的实现

在下面的实现中,Subject 维护了一组观察者,当它的状态发生变化时,会通过 Notify 方法通知所有的观察者,调用它们的 Update 方法。


实现代码

go
package main

import "fmt"

// Observer 观察者接口,定义了一个更新方法
type Observer interface {
	Update(state string)
}

// Subject 主题接口,定义了注册、移除和通知观察者的方法
type Subject interface {
	RegisterObserver(o Observer)
	RemoveObserver(o Observer)
	NotifyObservers()
}

// ConcreteSubject 具体主题,保存状态并通知观察者
type ConcreteSubject struct {
	state       string
	observers   []Observer
}

// RegisterObserver 注册观察者
func (s *ConcreteSubject) RegisterObserver(o Observer) {
	s.observers = append(s.observers, o)
}

// RemoveObserver 移除观察者
func (s *ConcreteSubject) RemoveObserver(o Observer) {
	for i, observer := range s.observers {
		if observer == o {
			s.observers = append(s.observers[:i], s.observers[i+1:]...)
			break
		}
	}
}

// NotifyObservers 通知所有观察者
func (s *ConcreteSubject) NotifyObservers() {
	for _, observer := range s.observers {
		observer.Update(s.state)
	}
}

// SetState 设置主题状态,并通知观察者
func (s *ConcreteSubject) SetState(state string) {
	s.state = state
	s.NotifyObservers()
}

// ConcreteObserver 具体观察者,响应主题状态变化
type ConcreteObserver struct {
	name string
}

// Update 观察者响应主题状态变化
func (o *ConcreteObserver) Update(state string) {
	fmt.Printf("%s received state change: %s\n", o.name, state)
}

func main() {
	// 创建主题
	subject := &ConcreteSubject{}

	// 创建观察者
	observer1 := &ConcreteObserver{name: "Observer 1"}
	observer2 := &ConcreteObserver{name: "Observer 2"}

	// 注册观察者
	subject.RegisterObserver(observer1)
	subject.RegisterObserver(observer2)

	// 修改主题状态,并通知观察者
	subject.SetState("State 1")

	// 移除一个观察者
	subject.RemoveObserver(observer1)

	// 修改主题状态,只有剩下的观察者会收到通知
	subject.SetState("State 2")
}

实现解析

  1. 观察者接口(Observer):定义了一个 Update 方法,每个具体的观察者都需要实现这个方法,以响应主题的状态变化。

    • Update(state string):主题状态发生变化时,观察者会调用这个方法来更新自己。
  2. 主题接口(Subject):定义了注册、移除观察者和通知观察者的方法。

    • RegisterObserver(o Observer):注册一个观察者。
    • RemoveObserver(o Observer):移除一个已注册的观察者。
    • NotifyObservers():通知所有注册的观察者,触发它们的 Update 方法。
  3. 具体主题(ConcreteSubject):实现了 Subject 接口,并且维护了一个观察者列表。当主题的状态发生变化时,调用 NotifyObservers() 方法通知所有观察者。

    • SetState(state string):设置主题的状态,并调用 NotifyObservers 通知观察者。
  4. 具体观察者(ConcreteObserver):实现了 Observer 接口,每个观察者在主题状态变化时执行 Update 方法。

    • Update(state string):当接收到主题的状态变化时,执行更新逻辑。

运行结果

bash
Observer 1 received state change: State 1
Observer 2 received state change: State 1
Observer 2 received state change: State 2
  • 当我们调用 SetState("State 1") 时,所有注册的观察者都会收到状态更新通知。
  • 当我们移除 Observer 1 后,再次调用 SetState("State 2") 时,只有 Observer 2 会接收到更新通知。

观察者模式的优缺点

优点

  1. 解耦:主题和观察者之间没有直接的依赖关系,观察者通过接口与主题进行通信,降低了代码的耦合度。
  2. 动态订阅:观察者可以在运行时注册或取消注册,灵活性高。
  3. 多对一通知:一个主题可以通知多个观察者,适合一对多的场景。

缺点

  1. 效率问题:当观察者数量较多时,通知所有观察者的效率可能较低。
  2. 通知遗漏:如果主题发生变化时,未及时通知所有观察者,可能导致某些观察者状态不一致。
  3. 复杂度增加:观察者模式可能会导致系统的复杂度增加,特别是在有大量观察者和复杂状态管理的情况下。

总结

观察者模式是一种非常有用的设计模式,它通过让对象在状态改变时自动通知依赖对象,解决了对象之间紧耦合的问题。Go 语言的接口和灵活的结构体使得实现观察者模式变得相对简洁。通过这个模式,可以轻松实现事件驱动的程序设计,适用于事件通知、消息传递等场景。