设计模式深度剖析:观察者模式的核心实践
模式定义
观察者模式(Observer Pattern)是行为型设计模式,定义对象间的一对多依赖关系,当被观察对象状态变化时,所有依赖对象自动收到通知并更新。该模式又称发布-订阅模式,是事件驱动系统的基石。
核心思想
松耦合通知机制:主题与观察者通过抽象接口交互
动态订阅关系:运行时自由添加/移除观察者
推拉模型选择:可传递完整数据或仅通知变化
事件广播传播:支持一对多的消息分发
适用场景
事件驱动架构(如GUI事件处理)
实时数据监控系统
分布式系统状态同步
消息通知系统(邮件、短信、推送)
模式结构
Subject:被观察对象,维护观察者列表
Observer:观察者接口,定义更新方法
ConcreteSubject:具体被观察对象
ConcreteObserver:具体观察者实现
PHP实现示例:用户注册通知系统
<?php
// 观察者接口
interface UserObserver {
public function onUserRegistered(User $user);
}
// 被观察对象:用户服务
class UserService {
private $observers = [];
public function attach(UserObserver $observer) {
$this->observers[] = $observer;
}
public function detach(UserObserver $observer) {
$key = array_search($observer, $this->observers);
if ($key !== false) {
unset($this->observers[$key]);
}
}
public function register(string $email, string $phone) {
// 模拟用户创建
$user = new User(uniqid(), $email, $phone);
// 通知观察者
foreach ($this->observers as $observer) {
$observer->onUserRegistered($user);
}
}
}
// 用户实体类
class User {
public function __construct(
public readonly string $id,
public readonly string $email,
public readonly string $phone
) {}
}
// 具体观察者:邮件服务
class EmailService implements UserObserver {
public function onUserRegistered(User $user) {
echo sprintf("发送欢迎邮件至:%s\n", $user->email);
}
}
// 具体观察者:短信服务
class SmsService implements UserObserver {
public function onUserRegistered(User $user) {
echo sprintf("发送验证短信至:%s\n", $user->phone);
}
}
// 客户端使用
$userService = new UserService();
$userService->attach(new EmailService());
$userService->attach(new SmsService());
// 新用户注册触发通知
$userService->register("user@example.com", "13800138000");
/* 输出:
发送欢迎邮件至:user@example.com
发送验证短信至:13800138000
*/
Go实现示例:气象监测站实时更新
package main
import (
"fmt"
"math/rand"
"time"
)
// 观察者接口
type Display interface {
Update(temp, humidity float64)
}
// 被观察对象
type WeatherStation struct {
displays []Display
stopChan chan struct{}
}
func NewWeatherStation() *WeatherStation {
return &WeatherStation{
stopChan: make(chan struct{}),
}
}
// 注册观察者
func (ws *WeatherStation) Register(d Display) {
ws.displays = append(ws.displays, d)
}
// 启动数据采集
func (ws *WeatherStation) Start() {
go func() {
ticker := time.NewTicker(2 * time.Second)
defer ticker.Stop()
for {
select {
case <-ticker.C:
// 生成随机气象数据
temp := 20 + rand.Float64()*10
humidity := 40 + rand.Float64()*20
// 通知所有观察者
for _, d := range ws.displays {
d.Update(temp, humidity)
}
case <-ws.stopChan:
return
}
}
}()
}
func (ws *WeatherStation) Stop() {
close(ws.stopChan)
}
// 具体观察者:手机显示
type MobileDisplay struct{}
func (m *MobileDisplay) Update(temp, humidity float64) {
fmt.Printf("手机端更新 → 温度: %.1f℃ 湿度: %.1f%%\n", temp, humidity)
}
// 具体观察者:大屏显示
type WallDisplay struct{}
func (w *WallDisplay) Update(temp, humidity float64) {
fmt.Printf("大屏显示更新 → Temp: %.1f°C Humi: %.1f%%\n", temp, humidity)
}
func main() {
station := NewWeatherStation()
// 注册显示设备
station.Register(&MobileDisplay{})
station.Register(&WallDisplay{})
// 启动数据采集
station.Start()
// 模拟运行
time.Sleep(5 * time.Second)
station.Stop()
}
/* 示例输出:
手机端更新 → 温度: 26.3℃ 湿度: 52.4%
大屏显示更新 → Temp: 26.3°C Humi: 52.4%
手机端更新 → 温度: 22.7℃ 湿度: 48.9%
大屏显示更新 → Temp: 22.7°C Humi: 48.9%
*/
模式优缺点
✅ 优点:
主题与观察者松耦合
支持动态添加观察者
遵循开闭原则
实现广播通信机制
❌ 缺点:
通知顺序不可控
循环引用风险
频繁更新可能影响性能
观察者卡顿会阻塞主题
不同语言实现差异
模式演进方向
事件总线升级
引入事件总线管理复杂订阅关系消息中间件集成
结合RabbitMQ/Kafka实现分布式观察者响应式扩展
使用RxPHP/RxGo实现流式处理优先级队列
为观察者添加执行优先级断线重连机制
增强观察者容错能力
观察者模式VS发布订阅模式
最佳实践指南
防止内存泄漏
PHP中及时detach无用观察者,Go注意channel关闭异常处理机制
为观察者添加错误处理回调防止雪崩效应
Go版本使用带缓冲channel或限流器消息去重优化
为事件添加唯一标识符性能监控
添加观察者执行时间统计
总结
观察者模式通过建立清晰的订阅-通知机制,实现了系统组件间的高效解耦。该模式在以下场景表现卓越:
需要实时状态同步
构建事件驱动架构
实现广播通信机制
处理多方数据依赖关系
PHP与Go的实现对比体现了不同语言的设计哲学:
PHP采用经典OOP实现,强调接口契约
Go利用协程和接口组合,展现并发优势
实际应用时需注意:
合理控制观察者数量
处理跨线程/协程的数据竞争
建立完善的错误处理机制
在简单场景与复杂架构间选择合适变体
掌握观察者模式的精髓在于理解"事件驱动"和"消息广播"的设计思想,这种解耦模式是现代分布式系统和复杂应用架构的重要基石。