Golang 设计模式:观察者模式

使用 Golang 实现观察者模式

观察者模式介绍

观察者模式是一种行为设计模式,它定义了一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

观察者模式中的角色

  1. 主题(Subject) :也称为发布者(Publisher)或可观察对象(Observable)。主题维护了一组观察者对象,并通知它们状态变化的事件。主题负责添加、删除观察者,以及通知观察者。
  2. 观察者(Observer) :也称为订阅者(Subscriber)。观察者是主题状态的依赖者,它定义了一个通知方法,当主题状态发生变化时,该方法会被调用。观察者模式允许任意数量的观察者订阅一个主题,而主题无需知道观察者的具体是谁。

场景

在生活中,随时随地可以找到观察者模式的例子;例如我们想要订阅新闻,可以通过新闻系统订阅感兴趣的新闻类别来接收相应的新闻通知。在这个场景中,我们就是角色中的观察者(订阅者),新闻系统是角色中的发布者,新闻系统中的某一类别的新闻是具体的发布者。具体如图所示

代码示例说明

以下是一个简单的 Golang 代码示例,以上述的新闻系统为例子,演示观察者模式:

Publisher(发布者)接口

go 复制代码
type Publisher interface {
    register(subscriber Subscriber)
    deRegister(subscriber Subscriber)
    notifyAll()
}

NewsPublisher(新闻发布者)结构体

go 复制代码
type NewsPublisher struct {
    subscriberList []Subscriber
    category       string
    newsNum        int64
}

func NewNewsPublisher(category string, newsNum int64) *NewsPublisher {
    return &NewsPublisher{category: category, newsNum: newsNum}
}

// 实现订阅者注册
func (np *NewsPublisher) register(s Subscriber) {
    np.subscriberList = append(np.subscriberList, s)
}

// 取消订阅
func (np *NewsPublisher) deRegister(s Subscriber) {
    np.subscriberList = remove(np.subscriberList, s)
}

// 通知
func (np *NewsPublisher) notifyAll() {
    fmt.Printf("%d %s News has been released\n", np.newsNum, np.category)
    for _, subscriber := range np.subscriberList {
       subscriber.update(np.newsNum, np.category)
    }
}

// 移除元素
func remove(subList []Subscriber, removeSub Subscriber) []Subscriber {
    subListLength := len(subList)
    for i, subscriber := range subList {
       //fmt.Println("=========="+removeSub.getName() == subscriber.getName())
       if removeSub.getName() == subscriber.getName() {
          // 与最后一个交换位置 直接移除最后一个 避免新切片创建 不涉及大规模元素移动
          subList[subListLength-1], subList[i] = subList[i], subList[subListLength-1]
          return subList[:subListLength-1]
       }
    }
    return subList
}

Subscriber(订阅者)接口

go 复制代码
type Subscriber interface {
    update(int64, string)
    getName() string
}

User(具体订阅者)结构体

go 复制代码
type User struct {
    name string
}

func (u *User) getName() string {
    return u.name
}

func (u *User) update(newsNum int64, subCategory string) {
    fmt.Printf("Sending %d %s News to %s\n", newsNum, subCategory, u.name)
}

主程序

go 复制代码
func main() {
    // 创建Sports新闻发布者
    sportsNewsPublisher := NewNewsPublisher("Sports", 2)

    // 创建两个订阅者
    subscriber1 := &User{name: "张三"}
    subscriber2 := &User{name: "李四"}

    // 订阅Sports新闻
    sportsNewsPublisher.register(subscriber1)
    sportsNewsPublisher.register(subscriber2)

    // 发布Sports新闻并通知订阅者
    sportsNewsPublisher.notifyAll()

    // 创建International新闻发布者
    internationalNewsPublisher := NewNewsPublisher("International", 1)

    // 创建两个订阅者
    subscriber3 := &User{name: "张三"}
    subscriber4 := &User{name: "李四"}

    // 订阅International新闻
    internationalNewsPublisher.register(subscriber3)
    internationalNewsPublisher.register(subscriber4)

    // 发布International新闻并通知订阅者
    internationalNewsPublisher.notifyAll()
}

输出

css 复制代码
2 Sports News has been released
Sending 2 Sports News to 张三
Sending 2 Sports News to 李四
1 International News has been released
Sending 1 International News to 张三
Sending 1 International News to 李四

上述代码中,NewsPublisher 充当发布者的角色,而 User 则充当订阅者的角色。通过 register 方法,订阅者可以订阅感兴趣的新闻类别,而通过 notifyAll 方法,发布者在发布新闻时通知所有订阅者进行更新。

总结

观察者模式通过松耦合的设计,使得发布者和订阅者可以独立演化,增强了系统的灵活性。在实际应用中,观察者模式被广泛应用于事件处理、UI开发等场景,帮助实现了更清晰、可维护的代码结构。

相关推荐
摇滚侠3 小时前
Spring Boot 3零基础教程,IOC容器中组件的注册,笔记08
spring boot·笔记·后端
程序员小凯5 小时前
Spring Boot测试框架详解
java·spring boot·后端
你的人类朋友6 小时前
什么是断言?
前端·后端·安全
程序员小凯7 小时前
Spring Boot缓存机制详解
spring boot·后端·缓存
i学长的猫8 小时前
Ruby on Rails 从0 开始入门到进阶到高级 - 10分钟速通版
后端·ruby on rails·ruby
用户21411832636028 小时前
别再为 Claude 付费!Codex + 免费模型 + cc-switch,多场景 AI 编程全搞定
后端
茯苓gao8 小时前
Django网站开发记录(一)配置Mniconda,Python虚拟环境,配置Django
后端·python·django
Cherry Zack8 小时前
Django视图进阶:快捷函数、装饰器与请求响应
后端·python·django
爱读源码的大都督9 小时前
为什么有了HTTP,还需要gPRC?
java·后端·架构
码事漫谈9 小时前
致软件新手的第一个项目指南:阶段、文档与破局之道
后端