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

Vim 调用外部命令学习笔记

Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...

Unity3D中Gfx.WaitForPresent优化方案

前言 在Unity中&#xff0c;Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染&#xff08;即CPU被阻塞&#xff09;&#xff0c;这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案&#xff1a; 对惹&#xff0c;这里有一个游戏开发交流小组&…...

React Native 开发环境搭建(全平台详解)

React Native 开发环境搭建&#xff08;全平台详解&#xff09; 在开始使用 React Native 开发移动应用之前&#xff0c;正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南&#xff0c;涵盖 macOS 和 Windows 平台的配置步骤&#xff0c;如何在 Android 和 iOS…...

MFC内存泄露

1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...

linux arm系统烧录

1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 &#xff08;忘了有没有这步了 估计有&#xff09; 刷机程序 和 镜像 就不提供了。要刷的时…...

Nuxt.js 中的路由配置详解

Nuxt.js 通过其内置的路由系统简化了应用的路由配置&#xff0c;使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…...

【碎碎念】宝可梦 Mesh GO : 基于MESH网络的口袋妖怪 宝可梦GO游戏自组网系统

目录 游戏说明《宝可梦 Mesh GO》 —— 局域宝可梦探索Pokmon GO 类游戏核心理念应用场景Mesh 特性 宝可梦玩法融合设计游戏构想要素1. 地图探索&#xff08;基于物理空间 广播范围&#xff09;2. 野生宝可梦生成与广播3. 对战系统4. 道具与通信5. 延伸玩法 安全性设计 技术选…...

Windows安装Miniconda

一、下载 https://www.anaconda.com/download/success 二、安装 三、配置镜像源 Anaconda/Miniconda pip 配置清华镜像源_anaconda配置清华源-CSDN博客 四、常用操作命令 Anaconda/Miniconda 基本操作命令_miniconda创建环境命令-CSDN博客...

掌握 HTTP 请求:理解 cURL GET 语法

cURL 是一个强大的命令行工具&#xff0c;用于发送 HTTP 请求和与 Web 服务器交互。在 Web 开发和测试中&#xff0c;cURL 经常用于发送 GET 请求来获取服务器资源。本文将详细介绍 cURL GET 请求的语法和使用方法。 一、cURL 基本概念 cURL 是 "Client URL" 的缩写…...

数学建模-滑翔伞伞翼面积的设计,运动状态计算和优化 !

我们考虑滑翔伞的伞翼面积设计问题以及运动状态描述。滑翔伞的性能主要取决于伞翼面积、气动特性以及飞行员的重量。我们的目标是建立数学模型来描述滑翔伞的运动状态,并优化伞翼面积的设计。 一、问题分析 滑翔伞在飞行过程中受到重力、升力和阻力的作用。升力和阻力与伞翼面…...