设计模式之观察者模式 (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}")

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


相关推荐
逆境不可逃4 小时前
【从零入门23种设计模式03】创建型之建造者模式(简易版与导演版)
java·后端·学习·设计模式·职场和发展·建造者模式
趣魂6 小时前
心跳信令通常不采用NACK机制
设计模式·软件工程·软件构建
逆境不可逃7 小时前
【从零入门23种设计模式01】创建型之工厂模式(简单工厂+工厂方法+抽象工厂)
java·spring·设计模式·简单工厂模式·工厂方法模式·抽象工厂模式·工厂模式
测试工坊8 小时前
内存泄漏自动检测(下):对症下药,5 种泄漏 5 种抓法
设计模式
逆境不可逃9 小时前
【从零入门23种设计模式02】创建型之单例模式(5种实现形式)
java·spring boot·后端·单例模式·设计模式·职场和发展
逆境不可逃9 小时前
【从零入门23种设计模式04】创建型之原型模式
java·后端·算法·设计模式·职场和发展·开发·原型模式
HrxXBagRHod18 小时前
三菱FX5U与3台三菱E700变频器专用协议通讯实战
设计模式
王解1 天前
Agent Team设计模式与思维:从单体智能到群体智慧
设计模式·ai agent
J_liaty1 天前
23种设计模式一状态模式
设计模式·状态模式