元素码农
基础
UML建模
数据结构
算法
设计模式
网络
TCP/IP协议
HTTPS安全机制
WebSocket实时通信
数据库
sqlite
postgresql
clickhouse
后端
rust
go
java
php
mysql
redis
mongodb
etcd
nats
zincsearch
前端
浏览器
javascript
typescript
vue3
react
游戏
unity
unreal
C++
C#
Lua
App
android
ios
flutter
react-native
安全
Web安全
测试
软件测试
自动化测试 - Playwright
人工智能
Python
langChain
langGraph
运维
linux
docker
工具
git
svn
🌞
🌙
目录
▶
设计原则
单一职责原则
开闭原则
里氏替换原则
依赖倒置原则
接口隔离原则
迪米特法则
▶
创建型模式
工厂方法模式
抽象工厂
单例模式
建造者模式
原型模式
▶
结构型模式
适配器模式
装饰器模式
代理模式
外观模式
组合模式
桥接模式
享元模式
▶
行为型模式
策略模式
观察者模式
命令模式
模板方法模式
状态模式
责任链模式
迭代器模式
中介者模式
访问者模式
备忘录模式
解释器模式
发布时间:
2025-03-21 15:22
↑
☰
# 观察者模式 ## 概述 观察者模式(Observer Pattern)是一种行为型设计模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。当主题对象发生变化时,它的所有依赖者(观察者)都会收到通知并自动更新。 ## 问题场景 在软件开发中,我们经常会遇到以下场景: 1. 一个对象的状态发生改变,需要通知其他对象 2. 一个对象的改变需要同时改变其他对象,但不知道具体有多少对象需要改变 3. 需要建立一种一对多的依赖关系 ## 解决方案 观察者模式通过以下方式解决这些问题: ```mermaid classDiagram class Subject { <<interface>> +attach(Observer) +detach(Observer) +notify() } class Observer { <<interface>> +update() } class ConcreteSubject { -observers: Observer[] -state +getState() +setState() } class ConcreteObserver { -subject: Subject -state +update() } Subject <|.. ConcreteSubject Observer <|.. ConcreteObserver ConcreteSubject o-- Observer ``` 主要角色: 1. 主题(Subject): 定义观察者集合,提供添加和删除观察者的方法 2. 观察者(Observer): 为那些在主题发生改变时需获得通知的对象定义一个更新接口 3. 具体主题(ConcreteSubject): 将有关状态存入具体观察者对象 4. 具体观察者(ConcreteObserver): 实现抽象观察者角色所要求的更新接口 ## 代码示例 ### 1. 基本实现 ```go // Observer 观察者接口 type Observer interface { Update(message string) } // Subject 主题接口 type Subject interface { Attach(observer Observer) Detach(observer Observer) Notify(message string) } // ConcreteSubject 具体主题 type ConcreteSubject struct { observers []Observer } func NewConcreteSubject() *ConcreteSubject { return &ConcreteSubject{ observers: make([]Observer, 0), } } func (s *ConcreteSubject) Attach(observer Observer) { s.observers = append(s.observers, observer) } func (s *ConcreteSubject) Detach(observer Observer) { for i, obs := range s.observers { if obs == observer { s.observers = append(s.observers[:i], s.observers[i+1:]...) break } } } func (s *ConcreteSubject) Notify(message string) { for _, observer := range s.observers { observer.Update(message) } } // ConcreteObserver 具体观察者 type ConcreteObserver struct { name string } func NewConcreteObserver(name string) *ConcreteObserver { return &ConcreteObserver{name: name} } func (o *ConcreteObserver) Update(message string) { fmt.Printf("Observer %s received: %s\n", o.name, message) } ``` ### 2. 实际应用示例 ```go // 以新闻订阅为例 // NewsSubscriber 新闻订阅者接口 type NewsSubscriber interface { Update(news string) } // NewsAgency 新闻机构接口 type NewsAgency interface { Subscribe(subscriber NewsSubscriber) Unsubscribe(subscriber NewsSubscriber) PublishNews(news string) } // NewsChannel 新闻频道 type NewsChannel struct { subscribers []NewsSubscriber } func NewNewsChannel() *NewsChannel { return &NewsChannel{ subscribers: make([]NewsSubscriber, 0), } } func (n *NewsChannel) Subscribe(subscriber NewsSubscriber) { n.subscribers = append(n.subscribers, subscriber) } func (n *NewsChannel) Unsubscribe(subscriber NewsSubscriber) { for i, sub := range n.subscribers { if sub == subscriber { n.subscribers = append(n.subscribers[:i], n.subscribers[i+1:]...) break } } } func (n *NewsChannel) PublishNews(news string) { for _, subscriber := range n.subscribers { subscriber.Update(news) } } // EmailSubscriber 邮件订阅者 type EmailSubscriber struct { email string } func NewEmailSubscriber(email string) *EmailSubscriber { return &EmailSubscriber{email: email} } func (s *EmailSubscriber) Update(news string) { fmt.Printf("Sending news to %s: %s\n", s.email, news) } // MobileSubscriber 手机订阅者 type MobileSubscriber struct { phone string } func NewMobileSubscriber(phone string) *MobileSubscriber { return &MobileSubscriber{phone: phone} } func (s *MobileSubscriber) Update(news string) { fmt.Printf("Sending news to %s: %s\n", s.phone, news) } // 使用示例 func main() { // 创建新闻频道 newsChannel := NewNewsChannel() // 创建订阅者 emailSub := NewEmailSubscriber("user@example.com") mobileSub := NewMobileSubscriber("+1234567890") // 订阅新闻 newsChannel.Subscribe(emailSub) newsChannel.Subscribe(mobileSub) // 发布新闻 newsChannel.PublishNews("Breaking News: Observer Pattern in Action!") // 取消订阅 newsChannel.Unsubscribe(mobileSub) // 再次发布新闻 newsChannel.PublishNews("Another News: Mobile subscriber won't receive this!") } ``` ## 适用场景 1. 一对多的依赖关系 - 当一个对象的改变需要同时改变其他对象 - 不知道具体有多少对象需要改变 2. 事件处理系统 - 需要实现事件处理机制 - 需要实现发布-订阅功能 3. 松耦合的系统设计 - 观察者和主题之间的抽象耦合 - 建立对象之间的动态关系 ## 优缺点 ### 优点 1. 松耦合设计 - 主题和观察者之间是抽象耦合 - 可以独立地改变主题或观察者 2. 支持广播通信 - 主题发送通知给所有观察者 - 无须指定特定的接收者 3. 符合开闭原则 - 增加新的观察者很容易 - 不需要修改原有代码 ### 缺点 1. 可能引起性能问题 - 通知所有观察者会花费时间 - 可能引起循环调用 2. 可能导致内存泄漏 - 观察者没有及时注销 - 观察者链过长 3. 通知顺序不确定 - 观察者的通知顺序不可预测 - 可能影响程序的行为 ## 实现要点 1. 观察者管理 - 注册和注销机制 - 观察者列表维护 2. 通知机制 - 通知的时机 - 通知的方式 3. 线程安全 - 并发访问控制 - 避免死锁 ## 相关模式 1. 中介者模式 - 通过中介者协调对象之间的交互 - 避免对象之间的直接耦合 2. 单例模式 - 主题对象通常是单例 - 确保主题的唯一性 3. 策略模式 - 可以动态改变主题的行为 - 提供不同的通知策略 ## 总结 观察者模式是一种常用的行为型设计模式,它通过建立对象之间的一对多依赖关系,实现了对象之间的松耦合设计。观察者模式的核心价值在于: 1. 解耦合 - 主题和观察者之间的松耦合 - 支持对象之间的动态关系 2. 可扩展性 - 易于添加新的观察者 - 不影响现有的代码 3. 通信机制 - 实现对象之间的自动通知 - 支持广播通信 在实际开发中,观察者模式常用于实现事件处理系统、用户界面更新、消息推送等场景。使用时需要注意性能问题和内存管理,确保观察者能够及时注销,避免内存泄漏。同时,也要考虑线程安全问题,在并发环境下正确处理观察者的注册和通知。