当前位置: 首页 > news >正文

Golang设计模式(四):观察者模式

观察者模式

什么是观察者

观察者模式(Observer Pattern):定义对象之间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。观察者模式的别名包括发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。观察者模式是一种对象行为型模式。

结构

  1. **Subject(主题):**保持一个观察者列表,提供添加、删除和通知观察者的方法。
  2. **Observer(观察者):**定义一个更新接口,使得在主题状态变化时得到通知。
  3. Concrete Subject(具体主题):实现Subject接口,存储状态,当状态发生改变时,通知所有观察者。
  4. Concrete Observer(具体观察者):实现Observer接口,根据主题更新来更新自己的状态。

基本流程

  1. 注册观察者:观察者向主题注册自己。
  2. 状态变更:主题的状态发生变化。
  3. 通知观察者:主题通过调用注册的观察者的方法来通知它们状态已变化。
  4. 更新观察者:观察者接收到通知后更新自己的状态。

优点

  1. 解耦:观察者模式能够将主题和观察者解耦,它们之间不需要知道对方的存在。
  2. 可扩展性:新增观察者时,不需要修改主题的代码,符合开闭原则。
  3. 动态交互:可以实现动态的交互,主题可以在运行时添加或删除观察者。

缺点

  1. 循环引用:如果不当使用,可能会导致循环引用,增加内存管理的难度。
  2. 性能问题:当观察者较多时,通知所有观察者可能会造成性能问题。
  3. 顺序不确定:观察者接收通知的顺序是不确定的,可能会导致不可预知的副作用。

使用场景

观察者模式通常用于构建松耦合的系统,其中一个对象(称为主题或发布者)可以通知多个其他对象(称为观察者或订阅者)关于状态的变化。

  1. 在线购物平台订单管理
    • 主题(Subject):订单系统,负责在订单状态更新时(如确认、发货、收货)广播变更事件。
    • 观察者(Observers):包括支付模块、库存管理、物流跟踪等,它们监听订单状态更新并执行相应操作。
  2. 图形用户界面(GUI)同步
    • 主题(Subject):文档管理系统,监控文档内容的更改并触发更新事件。
    • 观察者(Observers):界面组件如文本框、滚动条、状态栏等,它们接收更新事件并刷新显示。
  3. 模型-视图-控制器(MVC)架构
    • 主题(Subject):数据模型,实时更新数据状态并通知视图与控制器。
    • 观察者(Observers):视图界面和控制器逻辑,订阅数据变更,视图更新显示,控制器响应用户交互。
  4. 社交媒体内容更新
    • 主题(Subject):用户发布系统,当用户发布新推文或状态时触发通知。
    • 观察者(Observers):粉丝和关注者,他们接收到新内容的通知并更新自己的信息流。
  5. 股票交易实时系统
    • 主题(Subject):股票行情中心,实时监控并发布股票价格的变动。
    • 观察者(Observers):交易平台界面、分析工具、自动交易脚本等,它们根据行情变化进行决策和操作。
  6. 动态配置更新系统
    • 主题(Subject):配置服务器,负责维护应用配置并在配置更新时发送通知。
    • 观察者(Observers):应用服务和组件,它们监听配置变更并实时调整自身设置。

注意事项

  1. 避免循环引用:确保主题和观察者之间不会产生循环引用。
  2. 管理生命周期:合理管理主题和观察者的生命周期,避免内存泄漏。
  3. 线程安全:在多线程环境中使用观察者模式时,需要考虑线程安全问题

代码案例

package designpatternimport ("fmt""sync"
)// Observer 观察者接口
type Observer interface {Update() // Update方法用于接收主题状态变化的通知
}// ConcreteObserver 具体观察者
type ConcreteObserver struct {name string
}func (c *ConcreteObserver) Update() {fmt.Printf("%s is notified.\n", c.name) // 具体观察者接收到通知后的具体处理逻辑
}// Subject 主题接口
type Subject interface {RegisterObserver(observer Observer)    // 注册观察者DeregisterObserver(observer Observer)  // 注销观察者NotifyObservers()                      // 通知所有观察者
}// ConcreteSubject 具体主题
type ConcreteSubject struct {observers []Observer // 观察者列表state     int        // 主题状态mu        sync.Mutex // 互斥锁,用于保护并发访问
}// NewConcreteSubject 创建具体主题实例
func NewConcreteSubject() *ConcreteSubject {return &ConcreteSubject{observers: make([]Observer, 0),mu:        sync.Mutex{}, // 初始化互斥锁}
}func (cs *ConcreteSubject) RegisterObserver(observer Observer) {cs.mu.Lock()defer cs.mu.Unlock()cs.observers = append(cs.observers, observer) // 注册观察者到列表中
}func (cs *ConcreteSubject) DeregisterObserver(observer Observer) {cs.mu.Lock()defer cs.mu.Unlock()for i, ob := range cs.observers {if ob == observer {cs.observers = append(cs.observers[:i], cs.observers[i+1:]...) // 从观察者列表中注销观察者break}}
}func (cs *ConcreteSubject) NotifyObservers() {cs.mu.Lock()defer cs.mu.Unlock()for _, ob := range cs.observers {ob.Update() // 通知所有观察者主题状态变化}
}func (cs *ConcreteSubject) SetState(state int) {cs.mu.Lock()defer cs.mu.Unlock()cs.state = state // 设置主题状态cs.NotifyObservers() // 通知所有观察者主题状态变化
}func main() {// 在 main 函数中演示了具体的使用方法,创建具体主题实例,注册观察者,并设置主题状态,触发通知subject := NewConcreteSubject()ob1 := &ConcreteObserver{"ob1"}ob2 := &ConcreteObserver{"ob2"}subject.RegisterObserver(ob1)subject.RegisterObserver(ob2)subject.SetState(1)
}

模拟一个新闻发布网站

package mainimport ("fmt""sync"
)// 新闻类型
type NewsType intconst (Business NewsType = iotaTechnologySportsWorldEntertainment
)// 观察者接口
type Observer interface {Update(News)
}// 具体观察者结构体
type Subscriber struct {Name       stringInterests  map[NewsType]boolRegister   chan NewsTypeUnregister chan NewsType
}func NewSubscriber(name string) *Subscriber {return &Subscriber{Name:       name,Interests:  make(map[NewsType]bool),Register:   make(chan NewsType),Unregister: make(chan NewsType),}
}func (s *Subscriber) Update(news News) {if _, ok := s.Interests[news.Type]; ok {fmt.Printf("%s received news: %s\n", s.Name, news.Headline)}
}func (s *Subscriber) RegisterInterest(interest NewsType) {s.Register <- interests.Interests[interest] = true
}func (s *Subscriber) UnregisterInterest(interest NewsType) {s.Unregister <- interestdelete(s.Interests, interest)
}// 主题接口
type Subject interface {Attach(Observer)Detach(Observer)Notify(string)
}// 具体主题结构体
type NewsAgency struct {observers map[Observer]boolnews       chan Newsmu         sync.Mutex
}func NewNewsAgency() *NewsAgency {return &NewsAgency{observers: make(map[Observer]bool),news:      make(chan News),}
}func (a *NewsAgency) Attach(observer Observer) {a.mu.Lock()defer a.mu.Unlock()a.observers[observer] = true
}func (a *NewsAgency) Detach(observer Observer) {a.mu.Lock()defer a.mu.Unlock()delete(a.observers, observer)
}func (a *NewsAgency) Notify(headline string) {for observer, _ := range a.observers {news := News{Headline: headline}go observer.Update(news)}
}// 新闻结构体
type News struct {Headline stringType     NewsType
}func main() {// 创建新闻机构agency := NewNewsAgency()// 创建订阅者alice := NewSubscriber("Alice")bob := NewSubscriber("Bob")// 订阅兴趣alice.RegisterInterest(Business)alice.RegisterInterest(World)bob.RegisterInterest(Technology)bob.RegisterInterest(Entertainment)// 将订阅者作为观察者注册到新闻机构agency.Attach(alice)agency.Attach(bob)// 新闻发布agency.Notify("Big Corp acquired Small Tech for $1B")// 订阅者取消订阅bob.UnregisterInterest(Entertainment)// 再次新闻发布agency.Notify("New breakthrough in AI technology")
}
  • 定义了 NewsType 类型,用于区分不同类型的新闻。
  • Observer 接口有一个 Update 方法,用于接收新闻更新。
  • Subscriber 结构体代表具体的观察者,它包含订阅者的名字和兴趣,以及注册和注销兴趣的通道。
  • Subject 接口包含 AttachDetachNotify 方法。
  • NewsAgency 结构体代表具体的主题,它维护了一个观察者集合和一个发布新闻的通道。
  • News 结构体包含新闻的标题和类型。
  • main 函数中,我们创建了新闻机构和两个订阅者,将订阅者的兴趣注册到新闻机构,并模拟了新闻发布。

相关文章:

Golang设计模式(四):观察者模式

观察者模式 什么是观察者 观察者模式(Observer Pattern)&#xff1a;定义对象之间的一种一对多依赖关系&#xff0c;使得每当一个对象状态发生改变时&#xff0c;其相关依赖对象皆得到通知并被自动更新。观察者模式的别名包括发布-订阅&#xff08;Publish/Subscribe&#xf…...

huggingface 笔记:查看GPU占用情况

0 准备部分 0.1 创建虚拟数据 import numpy as npfrom datasets import Datasetseq_len, dataset_size 512, 512 dummy_data {"input_ids": np.random.randint(100, 30000, (dataset_size, seq_len)),"labels": np.random.randint(0, 1, (dataset_size…...

JavaSE 学习记录

1. Java 内存 2. this VS super this和super是两个关键字&#xff0c;用于引用当前对象和其父类对象 this 关键字&#xff1a; this 关键字用于引用当前对象&#xff0c;即调用该关键字的方法所属的对象。 主要用途包括&#xff1a; 在类的实例方法中&#xff0c;通过 this …...

HTML与CSS的学习

什么是HTML,CSS&#xff1f; HTML(HyperText Markup Language):超文本标记语言。 超文本:超越了文本的限制&#xff0c;比普通文本更强大。除了文字信息&#xff0c;还可以定义图片、音频、视频等 标记语言:由标签构成的语言 >HTML标签都是预定义好的。例如:使用<a>…...

【单片机】STM32F070F6P6 开发指南(一)STM32建立HAL工程

文章目录 一、基础入门二、工程初步建立三、HSE 和 LSE 时钟源设置四、时钟系统&#xff08;时钟树&#xff09;配置五、GPIO 功能引脚配置六、配置 Debug 选项七、生成工程源码八、生成工程源码九、用户程序下载 一、基础入门 f0 pack下载&#xff1a; https://www.keil.arm…...

源码编译安装Rsync数据同步

源码编译安装 RPM软件包&#xff1a;rpm -ivh 或者 yum -y install 需要开发编译工具gcc、gcc-c、make等... 源码包----开发工具gcc与make----》可以执行的程序-----》运行安装 •主要优点 –获得软件的最新版&#xff0c;及时修复bug –软件功能可按需选择/定制&#xff…...

SQL Server2019安装步骤教程(图文)_最新教程

一、下载SQL Server2019 1.到微软官网下载SQL Server Developer版本&#xff0c;官网当前的2019版本下载需要注册账号。 不想注册的朋友&#xff0c;可以选择从网盘下载&#xff1a;点击此处直接下载 2.下载之后先解压&#xff0c;解压后执行exe安装程序。打开之后的界面如下…...

【SpringBoot】SpringBoot中防止接口重复提交(单机环境和分布式环境)

&#x1f4dd;个人主页&#xff1a;哈__ 期待您的关注 目录 &#x1f33c;前言 &#x1f512;单机环境下防止接口重复提交 &#x1f4d5;导入依赖 &#x1f4c2;项目结构 &#x1f680;创建自定义注解 ✈创建AOP切面 &#x1f697;创建Conotroller &#x1f4bb;分布…...

零基础学Java(全170集)

课程概述 本课程旨在全面深化对 Java 语言的核心技术理解&#xff0c;并提升编程实践能力。课程内容涵盖以下关键领域&#xff1a; 掌握Java核心语法&#xff0c;为后续学习打下扎实的基础。熟练运用Java常用的类库与开发工具&#xff0c;提高开发效率与质量。解决面向对象编…...

摄像头应用测试

作者简介&#xff1a; 一个平凡而乐于分享的小比特&#xff0c;中南民族大学通信工程专业研究生在读&#xff0c;研究方向无线联邦学习 擅长领域&#xff1a;驱动开发&#xff0c;嵌入式软件开发&#xff0c;BSP开发 作者主页&#xff1a;一个平凡而乐于分享的小比特的个人主页…...

Golang框架HTTP客户端框架zdpgo_resty发送表单请求

核心代码 这里通过字典传递了一个简单的表单数据。 发送的是POST请求。 resp, err : client.R().SetFormData(map[string]string{"username": "jeeva","password": "mypass",}).Post("http://127.0.0.1:3333/login")fmt.P…...

【机器学习300问】99、多通道卷积神经网络在卷积操作时有哪些注意事项?

一、多通道卷积神经网络示例 还是以图像处理为例&#xff0c;如果你的目标不仅是分析灰度图像特性&#xff0c;还打算捕捉RGB彩色图像的特征。如下图&#xff0c;当面对一张66像素的彩色图像时&#xff0c;提及的“3”实际上是指红、绿、蓝三种颜色通道&#xff0c;形象地说&am…...

Rust之函数、单元测试

1、函数 类似于C函数。 1.1、普通函数 在Rust中&#xff0c;函数的定义使用fn关键字&#xff0c;后跟函数名、参数列表、返回类型和函数体。函数体由一系列语句组成&#xff0c;用于执行特定的操作和计算。 函数定义&#xff1a; 使用fn关键字定义函数&#xff0c;函数由函数…...

Linux环境下TensorFlow安装教程

TensorFlow是学习深度学习时常用的Python神经网络框 下面以Mask R-CNN 的环境配置为例&#xff1a; 首先进入官网&#xff1a;www.tensorflow.org TensorFlow安装的总界面&#xff1a; 新建anaconda虚拟环境&#xff1a; conda create -n envtf2 python3.8 &#xff08;Pyth…...

基于Open3D的点云处理19-模拟生成点云

如果没有设备,怎么得到点云进行学习研究呢,一般通过以下方法: 模型采样+增加噪声:简单方便,但结果比较理想与真实扫描不一致;光线投射:简单方便,可以模仿传感器的一个扫描视角Blensor点云仿真:能够模仿传感器本身的一些噪声,适合激光雷达和tof相机的仿真,传感器较少…...

安全分析[1]之网络协议脆弱性分析

文章目录 威胁网络安全的主要因素计算机网络概述网络体系结构 网络体系结构脆弱性分组交换认证与可追踪性尽力而为匿名与隐私对全球网络基础实施的依赖无尺度网络互联网的级联特性中间盒子 典型网络协议脆弱性IP协议安全性分析IPSec&#xff08;IP Security)IPv6问题 ICMP协议安…...

数据湖对比(hudi,iceberg,paimon,Delta)

Delta 数据湖 Delta 更新原理 update/delete/merge 实现均基于spark的join功能。 定位 做基于spark做流批一体的数据处理 缺点 本质为批处理。强绑定spark引擎。整体性能相较其他数据湖比较差 hudi 数据湖 hudi 更新原理 通过hudi自定义的主键索引hoodiekey 布隆过…...

基于ssm的蛋糕商城系统java项目jsp项目javaweb

文章目录 蛋糕商城系统一、项目演示二、项目介绍三、系统部分功能截图四、部分代码展示五、底部获取项目源码&#xff08;9.9&#xffe5;带走&#xff09; 蛋糕商城系统 一、项目演示 蛋糕商城管理系统 二、项目介绍 系统角色 : 管理员、用户 一&#xff0c;管理员 管理员有…...

vue3父组件使用ref获取子组件的属性和方法

在vue3中父组件访问子组件中的属性和方法是需要借助于ref: 1.<script setup> 中定义响应式变量 例如&#xff1a; const demo1 ref(null) 2.在引入的子组件标签上绑定ref属性的值与定义的响应式变量同名( <demo1 ref"demo1"/>)。 父组件代码如下&…...

加入MongoDB AI创新者计划,携手MongoDB共同开创AI新纪元

加入MongoDB AI创新者计划&#xff01; MongoDB对AI创新和初创企业的支持既全面又广泛&#xff01;无论您是领先的AI初创企业还是刚刚起步&#xff0c;MongoDB Atlas都是支持您愿景的最佳平台。 AI 初创者计划The AI Startup Track AI初创者计划为早期初创企业提供专属福利&…...

eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)

说明&#xff1a; 想象一下&#xff0c;你正在用eNSP搭建一个虚拟的网络世界&#xff0c;里面有虚拟的路由器、交换机、电脑&#xff08;PC&#xff09;等等。这些设备都在你的电脑里面“运行”&#xff0c;它们之间可以互相通信&#xff0c;就像一个封闭的小王国。 但是&#…...

C++_核心编程_多态案例二-制作饮品

#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为&#xff1a;煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例&#xff0c;提供抽象制作饮品基类&#xff0c;提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...

《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》

引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...

【JVM】- 内存结构

引言 JVM&#xff1a;Java Virtual Machine 定义&#xff1a;Java虚拟机&#xff0c;Java二进制字节码的运行环境好处&#xff1a; 一次编写&#xff0c;到处运行自动内存管理&#xff0c;垃圾回收的功能数组下标越界检查&#xff08;会抛异常&#xff0c;不会覆盖到其他代码…...

MMaDA: Multimodal Large Diffusion Language Models

CODE &#xff1a; https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA&#xff0c;它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构&#xf…...

智能在线客服平台:数字化时代企业连接用户的 AI 中枢

随着互联网技术的飞速发展&#xff0c;消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁&#xff0c;不仅优化了客户体验&#xff0c;还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用&#xff0c;并…...

cf2117E

原题链接&#xff1a;https://codeforces.com/contest/2117/problem/E 题目背景&#xff1a; 给定两个数组a,b&#xff0c;可以执行多次以下操作&#xff1a;选择 i (1 < i < n - 1)&#xff0c;并设置 或&#xff0c;也可以在执行上述操作前执行一次删除任意 和 。求…...

【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)

可以使用Sqliteviz这个网站免费编写sql语句&#xff0c;它能够让用户直接在浏览器内练习SQL的语法&#xff0c;不需要安装任何软件。 链接如下&#xff1a; sqliteviz 注意&#xff1a; 在转写SQL语法时&#xff0c;关键字之间有一个特定的顺序&#xff0c;这个顺序会影响到…...

《基于Apache Flink的流处理》笔记

思维导图 1-3 章 4-7章 8-11 章 参考资料 源码&#xff1a; https://github.com/streaming-with-flink 博客 https://flink.apache.org/bloghttps://www.ververica.com/blog 聚会及会议 https://flink-forward.orghttps://www.meetup.com/topics/apache-flink https://n…...

今日科技热点速览

&#x1f525; 今日科技热点速览 &#x1f3ae; 任天堂Switch 2 正式发售 任天堂新一代游戏主机 Switch 2 今日正式上线发售&#xff0c;主打更强图形性能与沉浸式体验&#xff0c;支持多模态交互&#xff0c;受到全球玩家热捧 。 &#x1f916; 人工智能持续突破 DeepSeek-R1&…...