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

相关推荐
iuyou️3 小时前
Spring Boot知识点详解
java·spring boot·后端
一弓虽3 小时前
SpringBoot 学习
java·spring boot·后端·学习
姑苏洛言3 小时前
扫码小程序实现仓库进销存管理中遇到的问题 setStorageSync 存储大小限制错误解决方案
前端·后端
光而不耀@lgy4 小时前
C++初登门槛
linux·开发语言·网络·c++·后端
方圆想当图灵4 小时前
由 Mybatis 源码畅谈软件设计(七):SQL “染色” 拦截器实战
后端·mybatis·代码规范
毅航4 小时前
MyBatis 事务管理:一文掌握Mybatis事务管理核心逻辑
java·后端·mybatis
我的golang之路果然有问题5 小时前
速成GO访问sql,个人笔记
经验分享·笔记·后端·sql·golang·go·database
柏油5 小时前
MySql InnoDB 事务实现之 undo log 日志
数据库·后端·mysql
写bug写bug7 小时前
Java Streams 中的7个常见错误
java·后端
Luck小吕7 小时前
两天两夜!这个 GB28181 的坑让我差点卸载 VSCode
后端·网络协议