Skip to content

观察者模式

引言

在软件系统中,对象状态变化常需触发连锁反应(如订单支付后更新库存、发送通知)。传统轮询机制导致资源浪费响应延迟。观察者模式通过建立发布-订阅机制,实现状态变化的精准广播,成为事件驱动架构的核心设计范式。

诞生背景

GoF在《设计模式》中提出观察者模式,解决三大痛点:

  • 强耦合:对象直接调用依赖方(如订单类调用库存类)
  • 动态依赖:运行时需增减监听对象(如临时添加审计服务)
  • 状态同步:多对象需实时获取核心对象变更(如仪表盘更新)

演进过程

  • Smalltalk MVC(1970s):Model-View间数据绑定首次实践
  • Java AWT事件模型(1997):标准化addActionListener()接口
  • 响应式编程(2010s):RxJava将观察者模式扩展为流处理范式

核心概念

  • 主题(Subject):维护观察者列表,提供注册/注销接口
  • 观察者(Observer):定义状态更新时的响应接口
  • 具体主题(ConcreteSubject):状态变更时通知所有注册观察者
  • 具体观察者(ConcreteObserver):实现业务响应逻辑

通用实现

Java 实现

java
// 观察者接口
interface Observer {
    void update(String message);
}

// 主题接口
interface Subject {
    void register(Observer o);
    void unregister(Observer o);
    void notifyObservers();
}

// 具体主题:订单系统
class OrderSystem implements Subject {
    private List<Observer> observers = new ArrayList<>();
    private String status;

    public void setStatus(String status) {
        this.status = status;
        notifyObservers();
    }

    @Override
    public void register(Observer o) {
        observers.add(o);
    }

    @Override
    public void unregister(Observer o) {
        observers.remove(o);
    }

    @Override
    public void notifyObservers() {
        for (Observer o : observers) {
            o.update("订单状态变更: " + status);
        }
    }
}

// 具体观察者:库存服务
class InventoryService implements Observer {
    @Override
    public void update(String message) {
        System.out.println("[库存系统] " + message);
    }
}

// 客户端
public class Client {
    public static void main(String[] args) {
        OrderSystem order = new OrderSystem();
        order.register(new InventoryService());
        order.setStatus("已支付"); // 自动触发通知
    }
}

PHP 实现

php
// 观察者接口
interface Observer {
    public function update(string $message): void;
}

// 主题接口
interface Subject {
    public function register(Observer $o): void;
    public function unregister(Observer $o): void;
    public function notifyObservers(): void;
}

// 具体主题:新闻发布器
class NewsPublisher implements Subject {
    private array $observers = [];
    private string $headline;

    public function setHeadline(string $headline): void {
        $this->headline = $headline;
        $this->notifyObservers();
    }

    public function register(Observer $o): void {
        $this->observers[] = $o;
    }

    public function unregister(Observer $o): void {
        $key = array_search($o, $this->observers);
        if ($key !== false) unset($this->observers[$key]);
    }

    public function notifyObservers(): void {
        foreach ($this->observers as $observer) {
            $observer->update($this->headline);
        }
    }
}

// 具体观察者:邮件订阅者
class EmailSubscriber implements Observer {
    public function update(string $message): void {
        echo "[邮件提醒] 最新新闻: $message\n";
    }
}

// 客户端
$publisher = new NewsPublisher();
$publisher->register(new EmailSubscriber());
$publisher->setHeadline("PHP 8.4 发布!"); // 自动触发通知

应用场景

  • 事件驱动系统:GUI按钮点击事件处理
  • 状态监控:服务器集群健康状态广播
  • 数据同步:数据库主从复制
  • 消息中间件:Kafka/RabbitMQ的发布订阅模型
  • 响应式UI:前端框架的数据绑定(如Vue.js)

案例:气象站数据广播

Java 实现

java
// 具体主题:气象站
class WeatherStation implements Subject {
    private float temperature;
    // 实现Subject接口(同前)...

    public void setTemperature(float temp) {
        this.temperature = temp;
        notifyObservers();
    }
}

// 具体观察者:手机APP
class MobileApp implements Observer {
    @Override
    public void update(String message) {
        System.out.println("[APP推送] 当前温度: " + message + "℃");
    }
}

// 客户端
WeatherStation station = new WeatherStation();
station.register(new MobileApp());
station.setTemperature(26.5f); // 触发广播

PHP 实现

php
// 具体主题:股票交易系统
class StockExchange implements Subject {
    private float $price;
    // 实现Subject接口(同前)...

    public function setPrice(float $price): void {
        $this->price = $price;
        $this->notifyObservers();
    }
}

// 具体观察者:交易机器人
class TradingBot implements Observer {
    public function update(string $message): void {
        echo "[交易机器人] 股价更新: $message\n";
    }
}

// 客户端
$exchange = new StockExchange();
$exchange->register(new TradingBot());
$exchange->setPrice(152.37); // 触发广播

优点

  • 解耦:主题与观察者无直接依赖
  • 动态关系:运行时增减观察者
  • 广播效率:一次状态变更通知多方
  • 开闭原则:新增观察者无需修改主题

缺点

  • 更新不可控:观察者可能引发链式更新
  • 性能损耗:大量观察者增加通知耗时
  • 调试困难:跨对象调用链难以追踪

扩展

  • 事件总线:全局事件中心管理跨模块通信(如EventBus)
  • 推拉模型
    • 推模式:主题主动发送数据
    • 拉模式:观察者按需从主题获取数据
  • 消息过滤:主题携带变更类型(如notify(EventType.UPDATE)

模式协作

  • 与中介者模式:观察者处理分布式通信,中介者集中协调
  • 与责任链模式:观察者可组成处理链(如日志→审计→告警)
  • 与状态模式:状态变更触发观察者通知

延伸思考

  • 分布式挑战:跨服务观察需结合消息队列(如Redis Pub/Sub)
  • 前端应用:React/Vue的响应式系统基于观察者原理
  • 性能优化:异步通知(线程池/事件循环)避免阻塞

总结

观察者模式是状态传播的神经网,通过解耦的发布-订阅机制,实现变更的精准触达。其核心价值在于:构建弹性通信架构支持动态事件响应。从GUI事件处理到微服务通信,该模式始终是实时系统设计的基石,彰显了“变化即消息”的架构哲学。