GO设计模式——21、观察者模式(行为型)

目录

[观察者模式(Observer Pattern)](#观察者模式(Observer Pattern))

观察者模式的核心角色:

优缺点

使用场景

注意事项

代码实现


观察者模式(Observer Pattern)

观察者模式(Observer Pattern)定义了对象间的一种一对多的依赖关系,使得当一个对象状态发生改变时,所有依赖于它的对象都可以自动得到通知并且被更新。观察者模式是一种一对多的关系,可以有任意个(1个或多个)观察者对象同时监听(观察)某一个对象。

观察者模式核心角色

  • 主题(Subject):负责维护一组观察者,并在状态改变时通知观察者。也称为被观察者或可观察者,它是具有状态的对象,并维护着一个观察者列表。主题提供了添加、删除和通知观察者的方法。
  • 观察者(Observer):观察者是接收主题通知的对象。观察者需要实现一个更新方法,当收到主题的通知时,调用该方法进行更新操作。
  • 具体主题(Concrete Subject):具体主题是主题的具体实现类。它维护着观察者列表,并在状态发生改变时通知观察者。
  • 具体观察者(Concrete Observer):具体观察者是观察者的具体实现类。它实现了更新方法,定义了在收到主题通知时需要执行的具体操作。

优缺点

(1)优点:

  • 观察者和被观察者是抽象耦合的。
  • 建立一套触发机制。

(2)缺点:

  • 如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
  • 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
  • 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。

使用场景

  • 一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
  • 一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
  • 一个对象必须通知其他对象,而并不知道这些对象是谁。
  • 需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象......,可以使用观察者模式创建一种链式触发机制。

注意事项

  • 避免循环引用。
  • 如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式。

代码实现

Go 复制代码
package main

import "fmt"

// 新闻发布系统,用户可以订阅感兴趣的新闻主题,并在新闻发布时收到通知。
// 希望能够实现一个观察者模式,使订阅者能够接收到相关新闻的通知。

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

// 具体主题
type NewsSubject struct {
    observers []Observer
    news      string
}

// 注册
func (n *NewsSubject) Register(observer Observer) {
    n.observers = append(n.observers, observer)
}

// 注销
func (n *NewsSubject) Unregister(observer Observer) {
    for i, o := range n.observers {
       if o == observer {
          n.observers = append(n.observers[:i], n.observers[i+1:]...)
          break
       }
    }
}

// 通知观察者
func (n *NewsSubject) Notify() {
    for _, observer := range n.observers {
       observer.Update(n)
    }
}

func (n *NewsSubject) SetNews(news string) {
    n.news = news
    n.Notify()
}

// 观察者接口
type Observer interface {
    Update(subject Subject)
}

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

func (s *Subscriber) Update(subject Subject) {
    newsSubject := subject.(*NewsSubject)
    fmt.Printf("[%s] 收到新闻通知:%s\n", s.name, newsSubject.news)
}

// 客户端代码
func main() {
    subject := &NewsSubject{}

    subscriber1 := &Subscriber{name: "订阅者1"}
    subject.Register(subscriber1)

    subscriber2 := &Subscriber{name: "订阅者2"}
    subject.Register(subscriber2)

    subject.SetNews("新闻1发布了")
    subject.SetNews("新闻2发布了")

    subject.Unregister(subscriber1)

    subject.SetNews("新闻3发布了")
}
相关推荐
在未来等你12 分钟前
AI Agent设计模式 Day 19:Feedback-Loop模式:反馈循环与自我优化
设计模式·llm·react·ai agent·plan-and-execute
zhuyasen2 小时前
Go Web 开发利器:如何让你的 Gin 服务拥有 Nginx 般的静态文件处理能力?
nginx·go·gin
爷_2 小时前
Golang: sqlc 和 goose 最佳实践
后端·go·全栈
百***48073 小时前
【Golang】slice切片
开发语言·算法·golang
q***92513 小时前
Windows上安装Go并配置环境变量(图文步骤)
开发语言·windows·golang
喵个咪4 小时前
go-kratos-admin 技术栈深度解析:为什么选 Golang+Vue3 这套组合?
vue.js·go
兵bing5 小时前
设计模式-访问者模式
设计模式·访问者模式
python零基础入门小白5 小时前
【万字长文】大模型应用开发:意图路由与查询重写设计模式(从入门到精通)
java·开发语言·设计模式·语言模型·架构·大模型应用开发·大模型学习
MC丶科6 小时前
Java设计模式漫画英雄宇宙-工厂模式 —Factory博士的“超级英雄制造机”!
java·设计模式·漫画
码一行6 小时前
Go.1.25.4 和 Go.1.24.10 发布了!!
后端·go