Golang学习笔记_41——观察者模式

news2025/3/14 15:33:56

Golang学习笔记_38——享元模式
Golang学习笔记_39——策略模式
Golang学习笔记_40——模版方法模式


文章目录

    • 一、核心概念
      • 1. 定义
      • 2. 解决的问题
      • 3. 核心角色
      • 4. 类图
    • 二、特点分析
    • 三、适用场景
      • 1. 股票价格监控系统
      • 2. 物联网设备状态监控
      • 3. 电商订单状态通知
    • 四、Go语言实现示例
      • 完整实现代码
      • 执行结果
    • 五、高级应用
      • 1. 异步通知机制(参考网页10)
      • 2. 事件过滤机制
    • 六、与其他模式对比
    • 七、实现建议
    • 八、典型应用


一、核心概念

1. 定义

观察者模式是一种行为型设计模式,建立对象间的一对多依赖关系,当一个对象(主题)状态改变时,自动通知所有依赖对象(观察者)。其核心特点包括:

  • 松耦合设计:主题与观察者通过接口交互()
  • 动态订阅:运行时增减观察者()
  • 广播通知:状态变化自动触发多对象更新()

2. 解决的问题

  • 状态同步:多个对象需实时获取核心对象状态变化
  • 事件驱动:构建解耦的事件响应系统()
  • 资源监控:如CPU使用率报警()

3. 核心角色

角色作用
Subject维护观察者列表,定义注册/移除/通知方法()
Observer定义更新接口,接收主题通知()
ConcreteSubject实现具体状态管理和通知逻辑
ConcreteObserver实现业务响应逻辑

4. 类图

观察者模式类图

@startuml
interface Subject {
    + Register(Observer)
    + Remove(Observer)
    + Notify()
}

interface Observer {
    + Update()
}

class ConcreteSubject {
    - state: int
    + SetState(int)
}

class ConcreteObserver {
    + Update()
}

Subject <|-- ConcreteSubject
Observer <|-- ConcreteObserver
ConcreteSubject "1" *-- "0..*" Observer
@enduml

二、特点分析

优点

  1. 解耦设计:主题与观察者无直接依赖()
  2. 动态扩展:支持运行时增减观察者()
  3. 事件驱动:适用于异步处理场景()

缺点

  1. 通知顺序:多个观察者的执行顺序不可控()
  2. 循环触发:不当设计可能引发循环调用()
  3. 性能开销:大量观察者时通知效率降低()

三、适用场景

1. 股票价格监控系统

// 股票主题
type StockSubject struct {
    observers []Observer
    price     float64
}

func (s *StockSubject) Register(o Observer) {
    s.observers = append(s.observers, o)
}

func (s *StockSubject) Notify() {
    for _, o := range s.observers {
        o.Update(s.price)
    }
}

// 手机APP观察者
type MobileApp struct{}

func (m *MobileApp) Update(price float64) {
    fmt.Printf("手机端收到股价更新: %.2f\n", price)
}

2. 物联网设备状态监控

// 设备状态主题
type DeviceMonitor struct {
    observers []Observer
    status    string
}

func (d *DeviceMonitor) SetStatus(status string) {
    d.status = status
    d.Notify()
}

// 运维系统观察者
type OpsSystem struct{}

func (o *OpsSystem) Update(status string) {
    if status == "ERROR" {
        fmt.Println("触发告警:设备异常")
    }
}

3. 电商订单状态通知

type OrderSubject struct {
    observers []Observer
    state     string
}

func (o *OrderSubject) Ship() {
    o.state = "SHIPPED"
    o.Notify()
}

// 短信通知观察者
type SMSNotifier struct{}

func (s *SMSNotifier) Update(state string) {
    if state == "SHIPPED" {
        fmt.Println("发送物流短信通知")
    }
}

四、Go语言实现示例

示例类图

完整实现代码

package observer_demo

import (
	"fmt"
	"sync"
	"time"
)

// OrderEvent 事件结构体
type OrderEvent struct {
	OrderId     string
	Amount      float64
	PaymentTime time.Time
	UserID      string
}

// Observer 观察者接口
type Observer interface {
	Handle(event OrderEvent) error
	Name() string
}

// OrderSubject 主题接口
type OrderSubject interface {
	Subscribe(observer Observer)
	UnSubscribe(observer Observer)
	Notify(event OrderEvent)
}

// OrderService 具体主题实现
type OrderService struct {
	Observer sync.Map // 使用现成安全的Map存储观察者
	wg       sync.WaitGroup
}

func (s *OrderService) Subscribe(observer Observer) {
	s.Observer.Store(observer.Name(), observer)
}

func (s *OrderService) UnSubscribe(observer Observer) {
	s.Observer.Delete(observer.Name())
}

func (s *OrderService) Notify(event OrderEvent) {
	s.Observer.Range(func(key, value interface{}) bool {
		observer := value.(Observer)
		s.wg.Add(1)

		go func(obs Observer) {
			defer s.wg.Done()

			// 带超时控制的处理
			done := make(chan struct{})
			go func() {
				defer close(done)
				if err := retry(3, time.Second, func() error {
					return obs.Handle(event)
				}); err != nil {
					fmt.Printf("[Error] %s处理失败: %v\n", obs.Name(), err)
				}
			}()
			select {
			case <-done:
				fmt.Printf("[Info] %s处理完成\n", obs.Name())
			case <-time.After(5 * time.Second):
				fmt.Printf("[Error] %s处理超时\n", obs.Name())
			}
		}(observer)
		return true
	})
	s.wg.Wait()
}

// 重试函数
func retry(attempts int, sleep time.Duration, fn func() error) error {
	if err := fn(); err != nil {
		if attempts--; attempts > 0 {
			time.Sleep(sleep)
			return retry(attempts, sleep, fn)
		}
		return err
	}
	return nil
}

// InventoryObserver 具体观察者实现
type InventoryObserver struct {
}

func (i *InventoryObserver) Name() string {
	return "InventoryObserver"
}

func (i *InventoryObserver) Handle(event OrderEvent) error {
	// 模拟库存扣减操作
	fmt.Printf("库存系统:订单%s扣减库存,用户%s,金额%.2f\n",
		event.OrderId, event.UserID, event.Amount)
	return nil
}

// LogisticsObserver 具体观察者实现:物流业务
type LogisticsObserver struct {
}

func (l *LogisticsObserver) Name() string {
	return "LogisticsObserver"
}

func (l *LogisticsObserver) Handle(event OrderEvent) error {
	// 模拟物流操作
	fmt.Printf("物流系统:订单%s发货,用户%s,金额%.2f\n",
		event.OrderId, event.UserID, event.Amount)
	return nil
}

// UserObserver 具体观察者实现,用户服务
type UserObserver struct {
}

func (u *UserObserver) Name() string {
	return "UserObserver"
}

func (u *UserObserver) Handle(event OrderEvent) error {
	// 模拟用户服务操作
	fmt.Printf("用户服务:订单%s创建成功,用户%s,金额%.2f\n",
		event.OrderId, event.UserID, event.Amount)
	return nil
}

func test() {
	// 创建主题
	orderService := &OrderService{}
	// 创建观察者
	inventoryObserver := &InventoryObserver{}
	logisticsObserver := &LogisticsObserver{}
	userObserver := &UserObserver{}
	// 注册观察者
	orderService.Subscribe(inventoryObserver)
	orderService.Subscribe(logisticsObserver)
	orderService.Subscribe(userObserver)

	// 模拟支付成功事件
	event := OrderEvent{
		OrderId:     "20230303123456",
		Amount:      2999.00,
		PaymentTime: time.Now(),
		UserID:      "1001",
	}

	// 通知观察者
	fmt.Println("=== 开始通知观察者 ===")
	orderService.Notify(event)
	fmt.Println("=== 所有通知处理完成 ===")
}

执行结果

=== RUN   Test_test
=== 开始通知观察者 ===
物流系统:订单20230303123456发货,用户1001,金额2999.00
[Info] LogisticsObserver处理完成
用户服务:订单20230303123456创建成功,用户1001,金额2999.00
[Info] UserObserver处理完成
库存系统:订单20230303123456扣减库存,用户1001,金额2999.00
[Info] InventoryObserver处理完成
=== 所有通知处理完成 ===
--- PASS: Test_test (0.00s)
PASS

五、高级应用

1. 异步通知机制(参考网页10)

func (w *WeatherStation) asyncNotify() {
    w.mu.Lock()
    observers := make([]Observer, len(w.observers))
    copy(observers, w.observers)
    w.mu.Unlock()

    var wg sync.WaitGroup
    for _, o := range observers {
        wg.Add(1)
        go func(obs Observer) {
            defer wg.Done()
            obs.Update(w.temp)
        }(o)
    }
    wg.Wait()
}

2. 事件过滤机制

type SmartObserver struct {
    lastTemp float64
}

func (s *SmartObserver) Update(temp float64) {
    if math.Abs(temp-s.lastTemp) > 1.0 {
        fmt.Println("温度变化超过1度")
        s.lastTemp = temp
    }
}

六、与其他模式对比

模式核心区别典型场景
中介者模式通过中介协调多对象交互复杂对象间通信
责任链模式请求沿链传递 vs 广播通知审批流程处理
发布订阅引入消息代理解耦更彻底()分布式系统消息传递

七、实现建议

  1. 线程安全:使用sync.Mutex保证并发安全()
  2. 批量通知:使用copy避免通知过程中列表变更(见示例)
  3. 性能优化:对于大量观察者采用批处理或异步通知()
  4. 防止泄漏:及时移除无效观察者()

八、典型应用

  1. GUI事件处理:如按钮点击事件监听()
  2. 微服务配置中心:配置变更通知服务集群()
  3. 游戏引擎:玩家状态变化触发多系统响应()

通过这种设计模式,可以构建出松耦合、易扩展的事件驱动系统。在实际Go开发中,结合channel特性还能实现更高效的观察者模式变体。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2311202.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

中原银行:从“小机+传统数据库”升级为“OceanBase+通用服务器”,30 +系统成功上线|OceanBase DB大咖说(十五)

OceanBase《DB 大咖说》第 15 期&#xff0c;我们邀请到了中原银行金融科技部数据团队负责人&#xff0c;吕春雷。本文为本期大咖说的精选。 吕春雷是一位资历深厚的数据库专家&#xff0c;从传统制造企业、IT企业、甲骨文公司到中原银行&#xff0c;他在数据库技术与运维管理…

游戏引擎学习第140天

回顾并为今天的内容做准备 目前代码的进展到了声音混音的部分。昨天我详细解释了声音的处理方式&#xff0c;声音在技术上是一个非常特别的存在&#xff0c;但在游戏中进行声音混音的需求其实相对简单明了&#xff0c;所以今天的任务应该不会太具挑战性。 今天我们会编写一个…

LeetCode热题100JS(44/100)第八天|二叉树的直径|二叉树的层序遍历|将有序数组转换为二叉搜索树|验证二叉树搜索树|二叉搜索树中第K小的元素

543. 二叉树的直径 题目链接&#xff1a;543. 二叉树的直径 难度&#xff1a;简单 刷题状态&#xff1a;1刷 新知识&#xff1a; 解题过程 思考 示例 1&#xff1a; 输入&#xff1a;root [1,2,3,4,5] 输出&#xff1a;3 解释&#xff1a;3 &#xff0c;取路径 [4,2,1,3] 或…

力扣刷题DAY6(滑动窗口/中等+栈/简单、中等)

一、滑动窗口 找到字符串中所有字母异位词 方法一&#xff1a;哈希表 class Solution { public:vector<int> findAnagrams(string s, string p) {vector<int> ans;unordered_map<char, int> target;for (int i 0; i < p.size(); i) {target[p[i]];}in…

虚拟机 | Ubuntu图形化系统: open-vm-tools安装失败以及实现文件拖放

系列文章目录 虚拟机 | Ubuntu 安装流程以及界面太小问题解决 文章目录 系列文章目录虚拟机 | Ubuntu 安装流程以及界面太小问题解决 前言一、VMware Tools 和 open-vm-tools 是什么1、VMware Tools2、open-vm-tools 二、推荐使用open-vm-tools&#xff08;简单&#xff09;1、…

【DeepSeek】Ubuntu快速部署DeepSeek(Ollama方式)

文章目录 人人都该学习的DeepSeekDeepSeek不同版本功能差异DeepSeek与硬件直接的关系DeepSeek系统兼容性部署方式选择部署步骤&#xff08;Ollama方式&#xff09;1.选定适合的deepseek版本2.环境准备3.安装Ollama4.部署deepseek5.测试使用 人人都该学习的DeepSeek DeepSeek 作…

升级到Android Studio 2024.2.2 版本遇到的坑

一、上来就编译报错&#xff0c;大概率是因为选择了替换安装&#xff0c;本地配置文件出错 找到本地当前版本的配置文件&#xff0c;删掉&#xff0c;重启studio就好了&#xff1a; 1、打开终端 2、“cd /Users/用户名/Library/Application\ Support/Google” //到Google目录 …

【ESP-ADF】在 VSCode 安装 ESP-ADF 注意事项

1.检查网络 如果您在中国大陆安装&#xff0c;请使用魔法上网&#xff0c;避免无法 clone ESP-ADF 仓库。 2.VSCode 安装 ESP-ADF 在 VSCode 左侧活动栏选择 ESP-IDF:explorer&#xff0c;展开 advanced 并点击 Install ESP-ADF 然后会出现选择 ESP-ADF 安装目录。 如果出现…

我的两个医学数据分析技术思路

我的两个医学数据分析技术思路 从临床上获得的或者公共数据库数据这种属于观察性研究&#xff0c;是对临床诊疗过程中自然产生的数据进行分析而获得疾病发生发展的规律等研究成果。再细分&#xff0c;可以分为独立危险因素鉴定和预测模型构建两种。 独立危险因素鉴定是一直以…

FPGA-DE2115开发板实现4位全加器、3-8译码器。

文章目录 一、安装quartus二、4位全加器三、3-8译码器&#xff08;8段数码管&#xff09;四、参考文章 一、安装quartus 安装quartus参考文章&#xff1a;Quartus Prime 18.0与ModelSim的安装 Quartus II 18.0安装教程&#xff08;非常详细&#xff09;从零基础入门到精通&…

Pytorch xpu环境配置 Pytorch使用Intel集成显卡

1、硬件集显要为Intel ARC并安装正确驱动 2、安装Intel oneAPI Base Toolkit &#xff08;https://www.intel.cn/content/www/cn/zh/developer/tools/oneapi/base-toolkit-download.html&#xff09;安装后大约20G左右&#xff0c;注意安装路径 3、安装Visual Studio Build To…

uploadlabs通关思路

目录 靶场准备 复现 pass-01 代码审计 执行逻辑 文件上传 方法一&#xff1a;直接修改或删除js脚本 方法二&#xff1a;修改文件后缀 pass-02 代码审计 文件上传 1. 思路 2. 实操 pass-03 代码审计 过程&#xff1a; 文件上传 pass-04 代码审计 文件上传 p…

优选算法的智慧之光:滑动窗口专题(二)

专栏&#xff1a;算法的魔法世界​​​​​​ 个人主页&#xff1a;手握风云 目录 一、例题讲解 1.1. 最大连续1的个数 III 1.2. 找到字符串中所有字母异位词 1.3. 串联所有单词的子串 1.4. 最小覆盖子串 一、例题讲解 1.1. 最大连续1的个数 III 题目要求是二进制数组&am…

【蓝桥杯单片机】第十二届省赛

一、真题 二、模块构建 1.编写初始化函数(init.c) void Cls_Peripheral(void); 关闭led led对应的锁存器由Y4C控制关闭蜂鸣器和继电器 由Y5C控制 2.编写LED函数&#xff08;led.c&#xff09; void Led_Disp(unsigned char ucLed); 将ucLed取反的值赋给P0 开启锁存器…

剑指 Offer II 047. 二叉树剪枝

comments: true edit_url: https://github.com/doocs/leetcode/edit/main/lcof2/%E5%89%91%E6%8C%87%20Offer%20II%20047.%20%E4%BA%8C%E5%8F%89%E6%A0%91%E5%89%AA%E6%9E%9D/README.md 剑指 Offer II 047. 二叉树剪枝 题目描述 给定一个二叉树 根节点 root &#xff0c;树的每…

第5章:vuex

第5章&#xff1a;vuex 1 求和案例 纯vue版2 vuex工作原理图3 vuex案例3.1 搭建vuex环境错误写法正确写法 3.2 求和案例vuex版细节分析源代码 4 getters配置项4.1 细节4.2 源代码 5 mapState与mapGetters5.1 总结5.2 细节分析5.3 源代码 6 mapActions与mapMutations6.1 总结6.2…

视觉在协作机器人上的场景应用

看了UR、ABB等协作机器人公司的一些视觉方面的应用&#xff0c;总结大概有下面几个方面。 1.工业制造领域 3C 产品生产 外观检测&#xff1a;可精确检测电子元件的划痕、污渍、凹陷等外观缺陷&#xff0c;如手机屏幕的微小划痕、芯片表面的瑕疵等&#xff0c;确保产品高质量&a…

MyBatis-Plus开发流程:Spring Boot + MyBatis-Plus 实现对 book_tab 表的增删改查及Redis缓存

前言 MyBatis-Plus 是一个 MyBatis 的增强工具&#xff0c;旨在简化开发、减少工作量。本文将介绍如何使用 Spring Boot 集成 MyBatis-Plus 来操作数据库&#xff0c;并结合 Redis 实现数据的缓存功能。 1项目搭建 1.1 创建 Spring Boot 项目 可以通过 Spring Initializr 快…

从开源大模型工具Ollama存在安全隐患思考企业级大模型应用如何严守安全红线

近日&#xff0c;国家网络安全通报中心通报大模型工具Ollama默认配置存在未授权访问与模型窃取等安全隐患&#xff0c;引发了广泛关注。Ollama作为一款开源的大模型管理工具&#xff0c;在为用户提供便捷的同时&#xff0c;却因缺乏有效的安全管控机制&#xff0c;存在数据泄露…

通过Docker搭个游戏——疯狂大陆(Pkland)

最近在研究我的服务器&#xff0c;在服务器上搭了很多docker的项目&#xff0c;然后找着找着发现一个能用Docker配置环境的游戏叫Pkland。 项目地址&#xff1a;GitHub - popkarthb/pkland: 疯狂大陆是一款多人在线的战略游戏。 游戏操作简捷,您仅需要使用浏览器就可以在任何时…