当前位置: 首页 > 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初创者计划为早期初创企业提供专属福利&…...

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…...

【入坑系列】TiDB 强制索引在不同库下不生效问题

文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...

OkHttp 中实现断点续传 demo

在 OkHttp 中实现断点续传主要通过以下步骤完成&#xff0c;核心是利用 HTTP 协议的 Range 请求头指定下载范围&#xff1a; 实现原理 Range 请求头&#xff1a;向服务器请求文件的特定字节范围&#xff08;如 Range: bytes1024-&#xff09; 本地文件记录&#xff1a;保存已…...

推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材)

推荐 github 项目:GeminiImageApp(图片生成方向&#xff0c;可以做一定的素材) 这个项目能干嘛? 使用 gemini 2.0 的 api 和 google 其他的 api 来做衍生处理 简化和优化了文生图和图生图的行为(我的最主要) 并且有一些目标检测和切割(我用不到) 视频和 imagefx 因为没 a…...

MFC 抛体运动模拟:常见问题解决与界面美化

在 MFC 中开发抛体运动模拟程序时,我们常遇到 轨迹残留、无效刷新、视觉单调、物理逻辑瑕疵 等问题。本文将针对这些痛点,详细解析原因并提供解决方案,同时兼顾界面美化,让模拟效果更专业、更高效。 问题一:历史轨迹与小球残影残留 现象 小球运动后,历史位置的 “残影”…...

站群服务器的应用场景都有哪些?

站群服务器主要是为了多个网站的托管和管理所设计的&#xff0c;可以通过集中管理和高效资源的分配&#xff0c;来支持多个独立的网站同时运行&#xff0c;让每一个网站都可以分配到独立的IP地址&#xff0c;避免出现IP关联的风险&#xff0c;用户还可以通过控制面板进行管理功…...

【 java 虚拟机知识 第一篇 】

目录 1.内存模型 1.1.JVM内存模型的介绍 1.2.堆和栈的区别 1.3.栈的存储细节 1.4.堆的部分 1.5.程序计数器的作用 1.6.方法区的内容 1.7.字符串池 1.8.引用类型 1.9.内存泄漏与内存溢出 1.10.会出现内存溢出的结构 1.内存模型 1.1.JVM内存模型的介绍 内存模型主要分…...

「全栈技术解析」推客小程序系统开发:从架构设计到裂变增长的完整解决方案

在移动互联网营销竞争白热化的当下&#xff0c;推客小程序系统凭借其裂变传播、精准营销等特性&#xff0c;成为企业抢占市场的利器。本文将深度解析推客小程序系统开发的核心技术与实现路径&#xff0c;助力开发者打造具有市场竞争力的营销工具。​ 一、系统核心功能架构&…...

【SpringBoot自动化部署】

SpringBoot自动化部署方法 使用Jenkins进行持续集成与部署 Jenkins是最常用的自动化部署工具之一&#xff0c;能够实现代码拉取、构建、测试和部署的全流程自动化。 配置Jenkins任务时&#xff0c;需要添加Git仓库地址和凭证&#xff0c;设置构建触发器&#xff08;如GitHub…...

消防一体化安全管控平台:构建消防“一张图”和APP统一管理

在城市的某个角落&#xff0c;一场突如其来的火灾打破了平静。熊熊烈火迅速蔓延&#xff0c;滚滚浓烟弥漫开来&#xff0c;周围群众的生命财产安全受到严重威胁。就在这千钧一发之际&#xff0c;消防救援队伍迅速行动&#xff0c;而豪越科技消防一体化安全管控平台构建的消防“…...