Observer (观察者) 模式
一个对象(称为 subject)维持一系列依赖于它(观察者)的对象,将有关状态的任何变更自动通知它们
可以使用以下组件来实现 Oberver 模式
- Subject(目标)
维护一系列的观察者,方便添加或删除观察者 - Observer(观察者)
为那些在目标状态发生改变时需要获得通知的对象提供一个更新接口 - ConcreteSubject(具体目标)
状态发生改变时,向 Observer 发出通知,储存 ConcreteObserver 的状态 - ConcreteObserver(具体观察者)
存储一个指向 ConcreteSubject 的引用,实现 Observer 的更新接口,以使自身状态于目标的状态保持一致。
1 | function ObserverList() { |
接下来,让我们模拟目标(Subject)和在观察者列表上添加、删除或通知观察者的能力
1 | function Subject() { |
如下是 HTML 代码:
1 | <button id="addNewObserver">Add Observer checkbox</button> |
如下是样例脚本:
1 | var controlCheckbox = document.getElementById("mainCheckbox"); |
Observer(观察者) 模式和 Publish/Subscribe(发布/订阅)模式的区别
Observer 模式要求希望接收到主题通知的观察者(或对象),必须订阅内容改变的事件
Publish/Subscribe 模式使用了一个主题/事件通道,这个通道介于希望接收到通知(订阅者)的对象和激活事件的对象(发布者)之间。该事件系统允许代码定义应用程序的特定事件,这些事件可以传递自定义参数,自定义参数包含订阅者所需的值。其目的是避免订阅者和发布者之间产生依赖关系。
这与 Observer 模式不同,因为它允许任何订阅者执行适当的事件处理程序来组测和接收发布者发出的通知。
下面这个示例说明了如果有 publish()、subscribe() 和 unsubscribe() 的功能实现,是如何使用 Publish/Subscribe 模式的:
1 | // 非常简单的 meail 处理程序 |
优点:
可以将应用程序分解为更小、更松散耦合的块,以改进代码管理和潜在的复用。
使用 Observer 模式背后的另一个动机是我们需要在哪里维护相关对象之间的一致性,而无需使类紧密耦合。例如,当一个对象需要能够通知其他对象,而无需在这些对象方面做假设时。
缺点:
例如,发布者可能会假设:一个或多个订阅者在监听它们。倘若我们假设订阅者需要记录或输出一些与应用程序处理有关的错误。如果订阅者执行日志崩溃了(或出现某种原因不能正常运行),由于系统解耦特性,发布者就不会看到这一点。
这个模式的另一个缺点是:订阅者非常无视彼此的存在,并对变换发布者产生的成本视而不见。由于订阅者和发布者之间的动态关系,很难跟踪依赖更新。
Publish/Subscribe 实现
Publish/Subscribe 非常适用于 JavaScript 生态系统,这主要是因为在其核心,ECMAScript 实现是由事件驱动的。在浏览器环境下尤其如此,因为 DOM 将事件是作为脚本编程的主要交互 API。
1 |
|
Publish/Subscribe 实现
- 先定义一个对象
- 通过立即执行函数给这个对象加载三个函数: publish, subscribers, unscribers
- 通过这个对象订阅一个主题
- 通过这个对象发布一个主题,订阅者发现这个主题有更新,调用内部函数处理更新的数据。
1 | var pubsub = {}; |
用户界面通知
接下来,假设我们有一个负责显示实时股票信息的 Web 应用程序。
该应用程序有一个显示股票统计和网格和一个显示最后更新点的计数器。当数据模型改变时,应用程序需要更新网格和计数器。在这种情况下,目标(它将发布主题/通知)就是数据模型,观察者就是网格和计数器。
再我们的实现中,订阅者会监听 newDataAvailable 这个 topic,以探测是否有新的股票信息。如果新通知发布到这个 topic,他将触发 gridUpdate 向包含股票信息的网格添加一个新行。他还将更新一个 last updated 计数器来记录最后一次添加的数据
1 |
|
使用Ben Alman 的 Pub/Sub 实现解耦应用程序
我们将使用Ben Alman在Publish/Subscribe模式上的jQuery实现来展示如何解耦一个用户界面。
1 | <!-- html模板 --> |
1 | ;(function($) { |
解耦基于Ajax的jQuery应用程序
如何使用Pub/Sub解耦在早期开发过程中的代码,以使我们省去一些可能繁琐的重构工作
下面的样例中,当用户表示他想进行搜索查询时,是如何发出一个topic通知的,以及当请求返回并且实际数据可用时,是如何发出另一个通知的。它让订阅者随后决定如何利用这些时间(或返回的数据)。它的好处是:如果我们愿意,我们可以有10个不同的订阅者以不同的方式使用返回的数据,但对于Ajax层面而言是无关紧要的。其唯一的责任是请求和返回数据,然后传递给任何一个想使用它的人。
1 | <form id="flickrSearch"> |
1 | (function($) { |