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开发等场景,帮助实现了更清晰、可维护的代码结构。

相关推荐
烛阴5 小时前
bignumber.js深度解析:驾驭任意精度计算的终极武器
前端·javascript·后端
服务端技术栈6 小时前
电商营销系统中的幂等性设计:从抽奖积分发放谈起
后端
你的人类朋友6 小时前
✍️Node.js CMS框架概述:Directus与Strapi详解
javascript·后端·node.js
面朝大海,春不暖,花不开6 小时前
自定义Spring Boot Starter的全面指南
java·spring boot·后端
钡铼技术ARM工业边缘计算机7 小时前
【成本降40%·性能翻倍】RK3588边缘控制器在安防联动系统的升级路径
后端
CryptoPP8 小时前
使用WebSocket实时获取印度股票数据源(无调用次数限制)实战
后端·python·websocket·网络协议·区块链
白宇横流学长8 小时前
基于SpringBoot实现的大创管理系统设计与实现【源码+文档】
java·spring boot·后端
草捏子8 小时前
状态机设计:比if-else优雅100倍的设计
后端
胡侃有料10 小时前
【设计模式】1.简单工厂、工厂、抽象工厂模式
设计模式·抽象工厂模式
考虑考虑10 小时前
Springboot3.5.x结构化日志新属性
spring boot·后端·spring