卤煮在大概一年前写过backbone的源码分析,里面讲的是对一些backbone框架的方法的讲解。这几天重新看了几遍backbone的源码,才发现之前对于它的理解不够深入,只关注了它的一些部分的细节和实现技巧。忽略了它的设计思想,而卤煮认为,一套库或者框架最值得借鉴的地方正好是它的设计思想。也巧,最近卤煮在读《设计模式与实践》这本书,所以温故知新,学以致用,打算写一篇博客算作这个系列的补充,以免将来忘记了代码时可以作为参考。

观察者模式

即使不用读源码,也知道backbone使用了观察者模式。因为它本身就是一套mvc框架,而mvc框架核心的就是m(模型)->v(视图)->c(控制器)的交互过程,观察者模式是驱动它们的核心模式之一。我们首先来回顾一下观察者模式的具体定义是如何的:

//定义对象之间的一种一对多的依赖关系,当一个对象的状态发生变化时,所有依赖于它的对象都将得到通知

观察者模式也可以称之为发布-订阅者模式,它需要一些订阅对象,通常它们会自己处理不同的逻辑,简单来说它们就是一些方法。这些方法需要被缓存起来,也就是说需要一个缓存对象缓存他们,等待需要的时候调用。然后我们还需要一个发布者,它负责发布通知,告诉程序,应该去读我们之前缓存起来的订阅对象了。概括起来,我们可以用几个关键字来概括这个模式: 一对多的关系-订阅-缓存订阅对象(通常是一个数组对象)-发布,以下是一个简单观察者模式:

//定义对象之间的一种一对多的依赖关系,当一个对象的状态发生变化时,所有依赖于它的对象都将得到通知
var Observer = (function() {
//缓存对象
var cache = []
, id = 0;
//添加订阅对象
function _listener(n, fn) {
var list = {
name: n,
bookid: ++id,
callback: fn
}
cache.push(list);
}
//发布消息
function _trigger() {
var name = Array.prototype.shift.call(arguments);
for (var i = 0; i < cache.length; i++) {
if (cache[i].name === name) {
cache[i].callback.apply(this, arguments);
}
}
}
//撤销监听
function _remove(fn) {
for (var i = 0; i < cache.length; i++) {
if (cache[i].callback === fn) {
cache.splice(1, 1);
break;
}
}
} return {
on: _listener,
trigger: _trigger,
remove: _remove
}
})(); function sayHello(h) {
console.log('hello :' + h);
}
function sayBonjour(h) {
console.log('bonjour :' + h);
}
function sayAligaduo(h) {
console.log('aligaduo: ' + h);
}
//订阅行为
Observer.on('say', sayHello);
Observer.on('say', sayBonjour);
Observer.on('say', sayAligaduo);
//发布事件
Observer.trigger('say', 'xiaoyi');
//取消订阅
Observer.remove(sayAligaduo);
Observer.trigger('say', 'xiaoyi222');

现在,我们来看一看backbone中的观察者模式是怎么样的。在backbone中,事件模块(Events)是核心模块之一,它提供了很多事件交互的方法,其中最常用也是最重要的是on,和trigger方法。其实,这个两个方法相当于设计模式中的订阅(on),发布(trigger),它们一个负责把方法存入对象,一个负责从对象中取出并执行函数。下面我们来看看具体的源码设计:

on和trigger分别负责订阅和发布功能,缓存对象这里指的是this._events,由于Events对象会extend到不同的模块中,所以this的指向是会更改的。因此,每一个this.events都代表着不同模块中的缓存对象。我们可以看到,trigger并不是真正的发布者,里面的triggerEvents才是真正的发布者,这里也使用了外观者模式,将正在的发布者(triggerEvents)隐藏了起来,接口的外观是trigger。backbone的Events是一个典型的观察者,它的实现几乎和示范代码一样,我们很容易一眼就将它认出来。

那么该模式在库中的具体应用场景呢?Events模块应用到的地方非常之多,我们这里举一个最常用的例子:使用过bakcbone框架的人都清楚,backbone和angluar的区别之一是,backbone需要自行实现数据和视图绑定,也就是说在VIEW初始化的时候,我们需要绑定对应model的关系,下面是一个VIEW初始化的时候需要处理的逻辑。代码表现如下:

此处,model作为订阅者订阅了一个名为change的方法,它指向的是view的render函数,它的意思可以翻译成model对view的一段对话:“喂,view老兄,把你的电话号码(change)给我吧,如果有需要的时候我会打(trigger)你的电话(change)告诉你接下来怎么做(render)”。接下来view老兄只需要需要静静的等待model打(trigger)它的电话(change)就行了,下面是model打电话的行为,源码如下:

以上trigger的调用是在set函数里面,而set函数在api中说明了,是在设置属性时用到的。因此,我们可以说,在为model,set一段属性时,它会触发绑定在该model上的change方法,change方法是在某个视图初始化的时候订阅到的,所以,一旦change事件发布,对应的订阅对象就会去响应,从而实现界面的刷新。当然,在这之前需要对属性做diff操作,以确保它们变化了。

Backbone源码解析(六):观察者模式应用的更多相关文章

  1. Celery 源码解析六:Events 的实现

    在 Celery 中,除了远程控制之外,还有一个元素可以让我们对分布式中的任务的状态有所掌控,而且从实际意义上来说,这个元素对 Celery 更为重要,这就是在本文中将要说到的 Event. 在 Ce ...

  2. Backbone源码解析(二):Model(模型)模块

    Model(模型)模块在bk框架中的作用主要是存储处理数据,它对外和对内都有很多操作数据的接口和方法.它与视图(Views)模块精密联系着,通过set函数改变数据结构从而改变视图界面的变化.下面我们来 ...

  3. Backbone源码解析(四):View(视图)模块

    View视图故名思义,它控制的是界面.我们可以把一个大的网页分成很多部分的视图,按照backbone的架构,每一个视图对应都是一个对象,我们可以通过元素的钩子(id或者class或者其他选择器)把它们 ...

  4. Backbone源码解析(三):Collection模块

    Collection模块式是对分散在项目中model的收集,他可以存储所有的model,构成一个集合,并且通过自身的方法统一操作model.Collection模块包装着若干对象,对象本身不具有一些方 ...

  5. Backbone源码解析(一):Event模块

    Backbone是一个当下比较流行的MVC框架.它主要分为以下几个模块: Events, View, Model, Collection, History, Router等几大模块.它强制依赖unde ...

  6. OpenJDK源码研究笔记(六)--观察者模式工具类(Observer和Observable)和应用示例

    本文主要讲解OpenJDK观察者模式的2个工具类,java.util.Observer观察者接口,java.util.Observable被观察者基类. 然后,给出了一个常见的观察者应用示例. Obs ...

  7. Backbone源码解析(五):Route和History(路由)模块

    今天是四月十二号,距离上次写博已经将近二十天了.一直忙于工作,回家被看书的时间占用了.连续两个礼拜被频繁的足球篮球以及各种体育运动弄的精疲力竭,所以很少抽时间来写技术博客.今天抽出时间把backbon ...

  8. AFNetworking (3.1.0) 源码解析 <六>

    这次继续介绍文件夹Serialization下的类AFURLResponseSerialization.这次介绍就不拆分了,整体来看一下.h和.m文件. 协议AFURLResponseSerializ ...

  9. ReactiveCocoa源码解析(六) SignalProtocol的take(first)与collect()延展实现

    上篇博客我们聊了observe().map().filter()延展函数的具体实现方式以及使用方式.我们在之前的博客中已经聊过,Signal的主要功能是位于SignalProtocol的协议延展中的, ...

随机推荐

  1. iPhone Anywehre虚拟定位提示“后台服务未启动,请重新安装应用后使用”的解决方法

    问题描述: iPhone越狱了,之后在Cydia中安装Anywhere虚拟定位,但是打开app提示:后台服务未启动,请重新安装应用后使用. 程序无法正常使用... 解决方法: 打开Cydia-已安装, ...

  2. UWP开发之Mvvmlight实践六:MissingMetadataException解决办法(.Net Native下Default.rd.xml配置问题)

    最近完成一款UWP应用,在手机端测试发布版(Release)的时候应用莫名奇妙的强行关闭,而同样的应用包在PC端一点问题都没有,而且Debug版在两个平台都没有问题,唯独手机的Release版有问题. ...

  3. Android 几种消息推送方案总结

    转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/6241354.html 首先看一张国内Top500 Android应用中它们用到的第三方推送以及所占数量: 现 ...

  4. FFmpeg 中AVPacket的使用

    AVPacket保存的是解码前的数据,也就是压缩后的数据.该结构本身不直接包含数据,其有一个指向数据域的指针,FFmpeg中很多的数据结构都使用这种方法来管理数据. AVPacket的使用通常离不开下 ...

  5. WebSocket - ( 一.概述 )

    说到 WebSocket,不得不提 HTML5,作为近年来Web技术领域最大的改进与变化,包含CSS3.离线与存储.多媒体.连接性( Connectivity )等一系列领域,而即将介绍的 WebSo ...

  6. 【干货分享】流程DEMO-请休假

    流程名: 请假申请  流程相关文件: 流程包.xml WebService业务服务.xml WebService.asmx WebService.cs  流程说明: 流程中集成了webservice服 ...

  7. Android Weekly Notes Issue #235

    Android Weekly Issue #235 December 11th, 2016 Android Weekly Issue #235 本期内容包括: 开发一个自定义View并发布为开源库的完 ...

  8. i++、++i 、i--、--i

    总结: i++ 先用后加, ++i先加后用: i--先用后减, --i先减后用: //int i = 1; //Console.WriteLine(i);//1 //Console.WriteLine ...

  9. IIS启动失败,启动Windows Process Activation Service时,出现错误13:数据无效 ;HTTP 错误 401.2 - Unauthorized 由于身份验证头无效,您无权查看此页

    因为修改过管理员账号的密码后重启服务器导致IIS无法启动,出现已下异常 1.解决:"启动Windows Process Activation Service时,出现错误13:数据无效&quo ...

  10. 解决托管在Windows上的Stash的Pull request无法合并的问题

    最近尝试合并一个托管在Windows的Stash系统中的pull request时,发现合并按钮被禁用,显示有冲突不能合并,但是在diff页面中没有现实冲突,而且代码实际上并没有任何冲突. 后来在这篇 ...