go

golang观察者模式

介绍

观察者模式(Observer Pattern)是一种行为设计模式,它用于在对象之间建立一对多的依赖关系,使得当一个对象的状态发生变化时,所有依赖于它的对象都能够自动收到通知并进行相应的更新。这种模式有时又称作发布-订阅模式、模型-视图模式。

涉及到的角色有:

  • Subject(主题):也称为被观察者或可观察者,它是具有状态的对象,可以被观察。它维护一组观察者对象,并在状态变化时通知观察者。
  • Observer(观察者):观察者是依赖于主题的对象,它定义了一个更新接口,用于接收主题的通知并进行相应的处理。
  • ConcreteSubject(具体主题):具体主题是主题的具体实现,它维护着一个状态,并在状态发生变化时通知观察者。
  • ConcreteObserver(具体观察者):具体观察者是观察者接口的具体实现,它实现了更新接口,以便在接收到主题通知时进行具体的处理。

file

原理:

  • 主题对象维护一个观察者列表,并提供方法用于注册、删除和通知观察者。
  • 当主题对象的状态发生变化时,它会遍历观察者列表,并依次调用每个观察者的更新方法。
  • 观察者对象在接收到主题的通知后,根据需要进行相应的处理,例如更新自身的状态、执行特定的操作等。

优点

  • 解耦性:观察者模式实现了主题和观察者之间的解耦,主题不需要知道观察者的具体细节,它们只需要通过定义好的接口进行通信。这样可以让主题和观察者之间的关系更加松散,提高系统的可扩展性和可维护性。

  • 可扩展性:通过使用观察者模式,可以方便地添加新的观察者,而无需修改现有的主题代码。这使得系统能够灵活地增加或减少观察者,以适应未来的需求变化。

  • 通知机制:观察者模式提供了一种简单而有效的通知机制。当主题的状态发生变化时,它会自动通知所有注册的观察者,观察者可以及时更新自身的状态或执行相应的操作。

  • 支持广播通信:主题可以同时通知多个观察者,实现一对多的通信方式。这使得观察者模式非常适用于需要将消息广播给多个对象的场景。

缺点

  • 内存泄漏风险:如果观察者没有正确地被移除,或者主题没有及时通知观察者取消订阅,可能会导致观察者对象无法被垃圾回收,从而造成内存泄漏的风险。

  • 更新顺序问题:观察者模式中观察者的更新顺序是不确定的,这可能会导致不同观察者之间的依赖关系出现问题。如果观察者之间有严格的顺序要求,开发人员需要自行处理这种情况。

  • 性能问题:当观察者较多时,通知所有观察者可能会带来一定的性能开销。如果观察者的处理逻辑非常复杂,可能会影响整体的性能。

  • 不适用于异步场景:观察者模式通常是同步的,主题和观察者之间是直接调用的关系。在异步场景下,可能需要额外的机制来处理异步通信和线程安全性。

使用场景

  • GUI事件处理:在图形用户界面(GUI)开发中,观察者模式常被用于处理用户与界面元素之间的交互。例如,当用户点击按钮或输入文本时,相关的观察者对象可以接收到通知并执行相应的操作,更新界面状态。

  • 发布-订阅系统:观察者模式可以用于实现发布-订阅系统,其中发布者是主题,而订阅者是观察者。发布者发布消息,而订阅者订阅感兴趣的消息类型,并在消息到达时接收通知。这种模式在消息队列、事件驱动系统和异步通信中广泛应用。

  • 消息通知系统:当需要在系统中传播消息或通知时,观察者模式可以提供一种有效的机制。例如,当新闻网站发布新的文章时,订阅了该网站的用户可以作为观察者接收到通知并查看新文章。

  • 数据库触发器:观察者模式可以用于数据库系统中的触发器实现。当数据库中的数据发生变化时,触发器可以作为观察者接收到通知,并执行相应的操作,例如更新其他表的数据或者触发其他业务逻辑。

  • 股票市场监测:在股票市场中,投资者可能对某些股票或指数的价格变化感兴趣。观察者模式可以用于实现股票价格的监测和通知机制,当关注的股票价格发生变化时,相关的观察者可以接收到通知并采取相应的投资策略。

  • 游戏开发:在游戏开发中,观察者模式常被用于处理游戏中的事件和状态变化。例如,当玩家角色受到伤害、获得道具或完成任务时,相关的观察者可以接收到通知并执行相应的游戏逻辑。

代码示例

package main

import "fmt"

// 观察者接口
type Observer interface {
    Update(msssage string)
}

// 主题接口
type Subject interface {
    Register(o Observer)
    Unregister(o Observer)
    Notify()
}

// 具体主题
type ConcreteSubject struct {
    observers []Observer
    message   string
}

// 订阅
func (c *ConcreteSubject) Register(o Observer) {
    c.observers = append(c.observers, o)
}

// 取消订阅
func (c *ConcreteSubject) Unregister(o Observer) {
    for i, observer := range c.observers {
        if observer == o {
            c.observers = append(c.observers[:i], c.observers[i+1:]...)
            break
        }
    }
}

// 通知观察者。Update方法用于接收主题发送的通知。
func (c *ConcreteSubject) Notify() {
    for _, observer := range c.observers {
        observer.Update(c.message)
    }
}

// 设置消息
func (c *ConcreteSubject) SetMessage(message string) {
    c.message = message
    c.Notify()
}

// 具体观察者
type ConcreteObserver struct {
    name string
}

func (c *ConcreteObserver) Update(message string) {
    fmt.Printf("Observer %s received message: %s\n", c.name, message)
}

func main() {
    object := &ConcreteSubject{}

    observer1 := &ConcreteObserver{name: "Observer 1"}
    observer2 := &ConcreteObserver{name: "Observer 2"}
    observer3 := &ConcreteObserver{name: "Observer 3"}

    object.Register(observer1)
    object.Register(observer2)
    object.Register(observer3)

    object.SetMessage("Hello, Observers!")

    object.Unregister(observer1)
    object.SetMessage("Goodbye, Observer 1!")
}

Observer Observer 1 received message: Hello, Observers!
Observer Observer 2 received message: Hello, Observers!
Observer Observer 3 received message: Hello, Observers!
Observer Observer 2 received message: Goodbye, Observer 1!
Observer Observer 3 received message: Goodbye, Observer 1!
分类: go
0 0 投票数
文章评分
订阅评论
提醒
guest

0 评论
最旧
最新 最多投票
内联反馈
查看所有评论

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部
0
希望看到您的想法,请您发表评论x