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发布了")
}
相关推荐
念何架构之路7 小时前
图解defer
开发语言·后端·golang
ん贤7 小时前
如何设计一个灵活、高效、安全的 AI 工具系统
人工智能·安全·go
旷世奇才李先生7 小时前
React 18\+TypeScript实战: hooks封装与组件设计模式
react.js·设计模式·typescript
白夜11177 小时前
C++设计模式(高内聚,低耦合)
c++·设计模式
ximu_polaris7 小时前
设计模式(C++)-结构型模式-桥接模式
c++·设计模式·桥接模式
楼田莉子7 小时前
仿muduo库的高并发服务器——正则表达式与any类介绍及其简单模拟实现
linux·服务器·c++·学习·设计模式
workflower7 小时前
机器人应用-室外区域巡逻
人工智能·设计模式·机器人·软件工程·软件构建
我喜欢山,也喜欢海9 小时前
Java和go在并发上的表现为什么不一样
java·python·golang
geovindu9 小时前
go: Flyweight Pattern
开发语言·设计模式·golang·享元模式
不会写DN10 小时前
其实跨域问题是后端来解决的? CORS
服务器·网络·面试·go