设计模式之观察者模式 (Observer Pattern)

📋 Research Summary

观察者模式是事件驱动架构的基础。从 React 的状态更新、Vue 的响应式系统、到消息队列(Kafka、RabbitMQ),都是其变体。它定义了对象间的一对多依赖,当一个对象状态改变时,所有依赖者自动收到通知。


🌱 逻辑原点

如果股票价格变动时需要同时更新行情面板、交易记录、风险监控系统、用户通知,你是让股票挨个调用这些系统,还是让这些系统自己"订阅"股票变动?

状态变化传播与系统可扩展性的矛盾:数据变化需要通知多个地方,但数据源不应该知道所有接收者是谁。

🧠 苏格拉底式对话

1️⃣ 现状:最原始的解法是什么?

让数据源直接调用所有需要通知的对象:

python 复制代码
class Stock:
    def __init__(self):
        self._price = 0
        self._dashboard = Dashboard()
        self._logger = Logger()
        self._risk_system = RiskSystem()
    
    def set_price(self, price):
        self._price = price
        # 直接调用所有接收者
        self._dashboard.update(price)
        self._logger.log(price)
        self._risk_system.check(price)

优点 :直接,流程清晰。
问题:数据源与所有接收者紧耦合,新增接收者需要修改数据源。

2️⃣ 瓶颈:规模扩大 100 倍时会在哪里崩溃?

当系统有 100 个不同的组件需要监听价格变动时:

  • 耦合爆炸:数据源需要引用 100 个接收者
  • 修改困难:新增一个接收者,数据源代码需要修改
  • 违反开闭原则:扩展功能需要修改已有代码
  • 性能问题:某些接收者处理慢,阻塞价格更新
  • 循环依赖风险:A 监听 B,B 又监听 A

核心矛盾:数据源不应该知道谁在监听,接收者应该能动态订阅和取消订阅。

3️⃣ 突破:必须引入什么新维度?

订阅-发布机制

不是"数据源主动调用接收者",而是"接收者订阅数据源,数据源只负责广播"。数据源维护一个订阅者列表,状态变化时遍历通知:

复制代码
Dashboard --订阅--> Stock <--订阅-- Logger
                        |
                        v
                    价格变化时广播给所有订阅者

这就是观察者的本质:将"推"模式转变为"拉-推"模式,实现发布者与订阅者的解耦

📊 视觉骨架

维护 maintain
订阅 subscribe
订阅 subscribe
通知 notify
通知 notify
主题 Subject
订阅者列表 Observer List
观察者A Observer A
观察者B Observer B

关键洞察:观察者模式让主题只依赖抽象的观察者接口,不依赖具体观察者。观察者可以动态订阅/取消订阅,主题完全不知道也不关心谁在监听。这实现了真正的解耦。

⚖️ 权衡模型

公式:

复制代码
Observer = 解决了状态变化的广播通知 + 牺牲了执行顺序确定性 + 增加了循环通知风险

代价分析:

  • 解决: 发布者与订阅者解耦、支持动态订阅、符合开闭原则、支持一对多通信
  • 牺牲: 执行顺序不确定、通知可能丢失(如果订阅者未及时处理)、调试困难
  • ⚠️ 增加: 循环通知风险、内存泄漏风险(忘记取消订阅)、订阅者执行时间影响发布者

使用建议:当一个对象的改变需要同时改变其他对象,且不知道具体有多少对象需要改变时使用。典型场景:事件系统、消息队列、MVC 中的模型-视图更新、响应式编程。

🔁 记忆锚点

python 复制代码
class Subject:
    """
    观察者的本质:订阅-发布机制
    """
    
    def __init__(self):
        self._observers: List[Observer] = []
    
    def attach(self, observer: "Observer") -> None:
        """订阅"""
        self._observers.append(observer)
    
    def detach(self, observer: "Observer") -> None:
        """取消订阅"""
        self._observers.remove(observer)
    
    def notify(self) -> None:
        """广播通知所有订阅者"""
        for observer in self._observers:
            observer.update(self)

class Stock(Subject):
    """具体主题:股票"""
    
    def __init__(self):
        super().__init__()
        self._price = 0
    
    @property
    def price(self) -> float:
        return self._price
    
    @price.setter
    def price(self, value: float) -> None:
        self._price = value
        self.notify()  # 状态变化时自动通知

# 观察者:只需要实现 update 方法
class Dashboard(Observer):
    def update(self, subject: Subject) -> None:
        if isinstance(subject, Stock):
            print(f"Dashboard updated: {subject.price}")

一句话本质: 观察者模式 = 订阅-发布机制,实现状态变化的自动广播


相关推荐
geovindu5 小时前
go: Proxy Pattern
开发语言·后端·设计模式·golang·代理模式
A-Jie-Y7 小时前
JAVA23种设计模式
java·设计模式
小程故事多_8010 小时前
Claude Code 全流程梳理,从需求输入到工具执行的完整逻辑
人工智能·设计模式·智能体·claude code·harness
cui178756813 小时前
排队免单模式:从爆火到优化,探寻实体商业新出路
大数据·人工智能·设计模式·个人开发·设计规范
医疗信息化王工13 小时前
26种不良事件表单的通用设计模式与实现
设计模式
mounter62514 小时前
【内核精进】Linux Kernel 设计模式(一):引用计数与可见性的艺术
linux·设计模式·linux kernel
workflower14 小时前
机器人应用-高空立面清洁
人工智能·深度学习·设计模式·机器人·软件工程·软件构建
likerhood14 小时前
设计模式:原型模式(Prototype Pattern)java版本
java·设计模式·原型模式
小程故事多_801 天前
从Claude Code源码中,拆解13个可直接复用的Agentic Harness设计模式(生产级实战解析)
人工智能·设计模式·智能体·claude code·harness
踩着两条虫1 天前
VTJ 平台六大设计模式落地实战指南
开发语言·前端·人工智能·低代码·设计模式·重构·架构