javascript观察者模式

观察者模式在javascript客户端编程中到处可见其踪影,非常重要的一种设计思想。特别我最近在接触angularjs后,更体会其中妙处。

搜索一下javascript观察者模式,经典的文章可谓不少。但还是想自己去整理一下思路,更为深刻去理解是原由。

在工作中,有时会更加重视实践价值,而忽视原理性知识,但当我们了解原理后,更能预测其结果和了解其本意,应用场景。

###介绍
在事件驱动的环境中,观察者模式(又名发布订阅模式(Publish - Subscribe)是一种管理人与其任务之间的关系(确切地讲,是对象及其行为和状态之间的关系)的得力工具。

它定义了一种一对多的关系,让多个观察者对象同时监听某一个主题对象,这个主题对象的状态发生变化时就会通知所有的观察者对象,使得它们能够自动更新自己。

###好处
将一个系统分割成一系列相互协作的类,但有一个常见的副作用:需要维护相关对象间的一致性。我们不希望为了维持一致性而使各类紧密耦合,因为这样降低了它们的可重用性。

  1. 支持简单的广播通信,自动通知所有已经订阅过的对象
  2. 页面载入后目标对象很容易与观察者存在一种动态关联,增加了灵活性
  3. 目标对象与观察者之间的抽象耦合关系能够单独扩展以及重用

###实践
先直接上代码(将代码先读懂),源代码链接


以上代码描述一个博客发布的场景。当博客作者发布新文章者时,订阅者都可以看到该文章,也可以进行相应的操作(比如胡扯一句)。

本例中的发布者是blogger实例(从observer扩展而来),拥有publish行为,通知所有订阅者。并且可以通过addSubscriber/removeSubscriber添加和删除订阅者。
在这,订阅者只是普通的回调函数,负责各自的行为动作。

###适用性
####事件监听器也是观察者
在DOM脚本脚本编程中,事件监听说到底就是一种内置的观察者,但事件处理器(handler)与事件监听器(listener)并不是一回事。前者说穿了就是把一种事件传给与其关联的函数的手段。而且这种模型中一种事件只能指定一个回调方法。而在监听器中,一个事件可以与几个监听器关联,每个监听器都能独立于其他监听器而改变。
例如,使用事件监听器,可以让多个函数响应同一个事件:
//example using listeners
var element = $(‘example’);
var fn1 = function(){
//handle click
}
var fn2 = function(){
//do other stuff with click
}
addEvent(element, ‘click’, fn1);
addEvent(element, ‘click’, fn2);
但用事件处理器就办不到:
//example using handlers
var element = document.getElementById(‘id’);
var fn1 = function(){
//handle click
}
var fn2 = function(){
//do other stuff with click
}
element.onclick = fn1;
element.onclick = fn2;
可见,监听器和观察者之间的共同之处显而易见。实际上互为同义语,它们都订阅特定的事件,然后等待事件的发生。事件发生后,订阅方的回调函数被执行。
####适用场合
。当一个抽象模型有两个方面,其中一个方面依赖于另一方面。将这二者封装在独立的对象中以使它们可以各自独立改变和复用
。当对一个对象的改变需要同时改变其它对象,而不知道具体有多少对象有待改变
。当一个对象必须通知其它对象,而它不能假定其它对象是谁。换言这,你不希望这些对象紧密耦合的。

就拿上一篇文章中的案例(angularjs五个最棒的特点)的例子。采用angularjs的$watch方法,就可以注册成某个属性或对象的监听者,一旦该对象发生变化,就会被通知到。而且可以在不同的模块中注册,案例中分别在Controller和Directive中都有注册,当菜单的总量变化时,能被立马执行。程序体也非常解耦,无需主动去调用某个对象通知其更新。

再前段时间在做的一个税费计算器项目,有很多的输入条件(面积,购入金额,区域等等)并且不同的输入控件(下拉单、输入框、单选等等)。传统的基于事件处理器的方式是,在不同的控件上注册对应的事件,例如,onchange, onclick等等,触发页面更新或重新计算税费的动作。
那么,对页面的更新和计算逻辑会混淆在一起。但如果以监听的方式,我们将各种输入组件成一个filter对象,然后用$watch监听该对象的变化。只要其中一个输入值发生变化,立马主动重新计算税费,也触发另外一个监听UI的变更对应的UI效果。

angularjs内部提供各种事件供监听,例如:$routeChangeStart(路由变更开始事件),这个事件就非常有用,我可以将先前在每个Controller中的判断用户是否登录的逻辑抽离到该事件中进行统一判断(因为Controller对应一个路由)
在后续,我会更详细介绍angularjs中观察者模式的应用,真的太酷了。

###小结
观察者模式是对应用系统进行抽象的非常有力的手段。可以定义一些事件供客户端使用,而不需要为此深入了解他们的代码。现在的WEB应用程序越来越大,在此背景下,作为一种提高代码的可维护性和简洁性的有力手段,可观察对象的作用更显突出。这种模式的应用有助力防止第三方开发人员因为对你的应用程序了解细节太多而把事情搞糟。