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发布了")
}
相关推荐
怕浪猫14 小时前
一行命令复刻爆款视频,我把 Hypit 从安装跑到了出片
人工智能·设计模式·程序员
云浪15 小时前
Go Context 到底是什么?
后端·go
newerp15 小时前
系统调用与 M/P 解绑
后端·程序员·go
newerp16 小时前
抢占式调度实现细节
后端·程序员·go
Zane199416 小时前
函数式编程里的函数,其实不是你天天写的那个函数——三大编程范式的边界在哪
设计模式
小小龙学IT17 小时前
Go 语言 net/http 网络编程深度解析:从 Handler 到生产级 HTTP 服务
go
指尖的爷17 小时前
ARM 架构 Ubuntu(RK3588/aarch64)go开发环境安装手册
ubuntu·架构·golang
名字还没想好☜17 小时前
Go 实现指数退避重试:context 取消、抖动 jitter 与什么时候别重试
后端·golang·go
我不会起名字32218 小时前
一天一道力扣Hot100(37):深度优先算法--括号生成
java·数据结构·c++·后端·python·算法·go
Casbin开源社区19 小时前
OpenAgent 详解:单二进制自托管 AI Agent 平台,30+ 模型接入、RAG 知识库、MCP 工具调用与 Casbin 工具权限
人工智能·golang·开源