观察者模式(Observer Pattern)
观察者模式是一种行为型设计模式,定义了对象之间的一对多依赖关系。当一个对象(被观察者)的状态发生改变时,所有依赖它的对象(观察者)都会自动收到通知并更新。观察者模式实现了主题(Subject)与观察者(Observer)之间的松耦合,使得双方可以独立变化。在 GoF(Gang of Four)的23种设计模式中,观察者模式被归类为行为型模式,它解决的核心问题是对象之间的通信与协调。观察者模式又被称为发布-订阅模式(Publish-Subscribe Pattern)的简化版本,但在严格定义中两者存在区别。
Subject 主题(被观察者)
Subject 是观察者模式中的核心角色,也被称为被观察者(Observable)或主题(Subject)。Subject 持有数据或状态,维护一个观察者列表,并提供注册(subscribe)和注销(unsubscribe/detach)观察者的方法。当 Subject 的状态发生变化时,它会遍历观察者列表并调用每个观察者的 update 方法进行通知。在本工具中,StockMarket 就是 Subject 的具体实现,它持有股价数据并在股价变化时通知所有已注册的投资者(Observer)。一个好的 Subject 实现应该只负责维护观察者列表和发送通知,不应包含过多的业务逻辑。
Observer 观察者
Observer 是观察者模式中的订阅者角色,定义了接收通知的统一接口。所有具体的观察者都必须实现 Observer 接口中的 update 方法,以便在被观察者状态变化时接收通知。Observer 的核心职责是在收到通知后执行相应的更新逻辑,例如更新界面显示、触发业务流程或记录日志。在本工具中,Observer 对应的是投资者角色,每个投资者在收到股价更新通知后会执行自己的处理逻辑。观察者模式要求 Observer 依赖于 Subject 的抽象接口,而非具体实现,这遵循了依赖倒置原则。
ConcreteSubject 具体被观察者
ConcreteSubject 是 Subject 的具体实现类,它维护了实际的业务状态,并在状态变化时通知所有注册的观察者。ConcreteSubject 通常包含具体的数据存储逻辑和状态变更方法。在本工具中,StockMarket 就是一个 ConcreteSubject,它维护了股价属性,提供了设置新股价和随机波动的方法。ConcreteSubject 在调用 notify 方法时,会将自身或变化的数据传递给每个 Observer 的 update 方法。理解 ConcreteSubject 的实现细节对于掌握观察者模式至关重要。
ConcreteObserver 具体观察者
ConcreteObserver 是 Observer 接口的具体实现类,它实现了 update 方法中具体的业务逻辑。每个 ConcreteObserver 可能有不同的更新行为,但它们都遵循相同的 Observer 接口。在本工具中,每个被添加到列表中的投资者都是一个 ConcreteObserver,它们在收到 StockMarket 的通知后会记录或处理最新的股价信息。多个 ConcreteObserver 可以同时订阅同一个 Subject,当 Subject 状态变化时,所有 ConcreteObserver 都会收到通知,这就是观察者模式一对多依赖关系的体现。
Push 模式(推模式)
Push 模式是观察者模式中 Subject 通知 Observer 的一种策略。在推模式下,Subject 在调用 Observer 的 update 方法时,会将变化后的完整数据作为参数直接传递给 Observer。Observer 被动接收数据,无需知道数据的来源或获取方式。推模式的优点是使用简单、Observer 不需要了解 Subject 的接口;缺点是数据格式由 Subject 决定,Observer 无法按需获取数据,可能导致传递了不必要的数据或缺少需要的数据。在本工具中,切换到 Push 模式后,StockMarket 在通知投资者时会直接传递最新的股价数据。
Pull 模式(拉模式)
Pull 模式是与 Push 模式对应的另一种通知策略。在拉模式下,Subject 在通知 Observer 时仅传递最少的信息(如通知本身或 Subject 的引用),Observer 需要主动调用 Subject 的接口来获取所需的数据。拉模式的优点是 Observer 可以按需获取数据、耦合度更低、灵活性更高;缺点是 Observer 需要了解 Subject 的接口,增加了实现的复杂性。在本工具中,切换到 Pull 模式后,StockMarket 仅通知投资者发生了价格变化,投资者如果想知道最新股价需要主动查询 StockMarket。
发布订阅模式(Publish-Subscribe Pattern)
发布订阅模式经常与观察者模式混淆,但两者在架构层面存在本质区别。在观察者模式中,Subject 直接持有 Observer 的引用,通知是同步或半同步的。在发布订阅模式中,发布者(Publisher)和订阅者(Subscriber)之间引入了一个中间层(事件调度中心或消息代理),发布者不直接持有订阅者的引用,而是通过中间层进行解耦。这种架构使得发布者和订阅者可以完全独立,甚至可以在不同的进程或服务器上运行。JavaScript 中的 EventEmitter 和 Node.js 的事件系统更接近发布订阅模式的实现。理解两者的区别有助于在实际项目中选择合适的事件通信方案。
事件驱动架构(Event-Driven Architecture)
事件驱动架构是一种软件架构模式,系统中的组件通过产生和响应事件来进行通信和协作。观察者模式是实现事件驱动架构的基础设计模式之一。在事件驱动架构中,事件的生产者(Event Producer)发出事件,事件的消费者(Event Consumer)监听并响应事件,两者通过事件总线(Event Bus)或消息代理(Message Broker)进行解耦。现代前端框架如 React 的状态管理(Redux/MobX)、Vue 的响应式系统以及 Node.js 的异步 I/O 模型,都深受事件驱动架构思想的影响。掌握观察者模式是理解事件驱动架构的重要第一步。
依赖倒置原则(Dependency Inversion Principle)
依赖倒置原则是 SOLID 五大设计原则之一,它要求高层模块不应该依赖低层模块,两者都应该依赖抽象。观察者模式很好地体现了依赖倒置原则:Subject(高层模块)依赖于 Observer 接口(抽象),而非具体的 Observer 实现(低层模块)。在本工具中,StockMarket 依赖于 Observer 接口来维护观察者列表,而不需要知道具体的投资者类型是什么。这种设计使得我们可以随时添加新的观察者类型而不必修改 StockMarket 的代码,完全符合开放-封闭原则。
内存泄漏与订阅管理
在观察者模式的实际应用中,内存泄漏是一个需要特别注意的问题。如果观察者订阅了被观察者但忘记在适当时机取消订阅,被观察者会一直持有对观察者的引用,导致观察者无法被垃圾回收。在单页应用(SPA)中,反复创建和销毁组件时尤其容易出现此类问题。本工具提供的清除观察者和重置功能,就是为了帮助开发者理解订阅管理的重要性。在实际开发中,应该养成在组件销毁时取消所有订阅的良好习惯,或者使用 WeakRef 等技术手段来避免内存泄漏。
响应式编程(Reactive Programming)
响应式编程是一种基于数据流和变化传播的编程范式,与观察者模式有着密切的联系。在响应式编程中,当数据源发生变化时,所有依赖该数据的计算和视图会自动更新。RxJS、MobX、Vue 的响应式系统等都是响应式编程的典型实现。观察者模式是响应式编程的基础模式,但响应式编程在此基础上引入了更多的概念如操作符、调度器、背压处理等。通过学习观察者模式实验室,开发者可以建立起对响应式编程的直觉认知,为后续深入学习 RxJS 等库打下坚实基础。
UD5工具箱