深入理解JavaScript系列(38):设计模式之职责链模式详解


Posted in Javascript onMarch 04, 2015

介绍

职责链模式(Chain of responsibility)是使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系。将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理他为止。

也就是说,请求以后,从第一个对象开始,链中收到请求的对象要么亲自处理它,要么转发给链中的下一个候选者。提交请求的对象并不明确知道哪一个对象将会处理它——也就是该请求有一个隐式的接受者(implicit receiver)。根据运行时刻,任一候选者都可以响应相应的请求,候选者的数目是任意的,你可以在运行时刻决定哪些候选者参与到链中。

正文

对于JavaScript实现,我们可以利用其原型特性来实现职责链模式。

var NO_TOPIC = -1;

var Topic;
function Handler(s, t) {

    this.successor = s || null;

    this.topic = t || 0;

}
Handler.prototype = {

    handle: function () {

        if (this.successor) {

            this.successor.handle()

        }

    },

    has: function () {

        return this.topic != NO_TOPIC;

    }

};

Handler只是接受2个参数,第一个是继任者(用于将处理请求传下去),第二个是传递层级(可以用于控制在某个层级下是否执行某个操作,也可以不用),Handler原型暴露了一个handle方法,这是实现该模式的重点,先来看看如何使用上述代码。
var app = new Handler({

        handle: function () {

            console.log('app handle');

        }

    }, 3);
    var dialog = new Handler(app, 1);
    var button = new Handler(dialog, 2);
    button.handle();

改代码通过原型特性,调用代码从button.handle()->dialog.handle()->app.handle()->参数里的handle(),前三个都是调用原型的handle,最后才查找到传入的参数里的handle,然后输出结果,也就是说其实只有最后一层才处理。

那如何做到调用的时候,只让dialog的这个对象进行处理呢?其实可以定义dialog实例对象的handle方法就可以了,但需要在new button的之前来做,代码如下:

var app = new Handler({

        handle: function () {

            console.log('app handle');

        }

    }, 3);
    var dialog = new Handler(app, 1);

    dialog.handle = function () {

        console.log('dialog before ...')

        // 这里做具体的处理操作

        console.log('dialog after ...')

    };
    var button = new Handler(dialog, 2);
    button.handle();

该代码的执行结果即时dialog.handle里的处理结果,而不再是给app传入的参数里定义的handle的执行操作。

那能不能做到自身处理完以后,然后在让继任者继续处理呢?答案是肯定的,但是在调用的handle以后,需要利用原型的特性调用如下代码:

Handler.prototype.handle.call(this);

该句话的意思说,调用原型的handle方法,来继续调用其继任者(也就是successor )的handle方法,以下代码表现为:button/dialog/app三个对象定义的handle都会执行。
var app = new Handler({

    handle: function () {

        console.log('app handle');

    }

}, 3);
var dialog = new Handler(app, 1);

dialog.handle = function () {

    console.log('dialog before ...')

    // 这里做具体的处理操作

    Handler.prototype.handle.call(this); //继续往上走

    console.log('dialog after ...')

};
var button = new Handler(dialog, 2);

button.handle = function () {

    console.log('button before ...')

    // 这里做具体的处理操作

    Handler.prototype.handle.call(this);

    console.log('button after ...')

};
button.handle();

通过代码的运行结果我们可以看出,如果想先自身处理,然后再调用继任者处理的话,就在末尾执行Handler.prototype.handle.call(this);代码,如果想先处理继任者的代码,就在开头执行Handler.prototype.handle.call(this);代码。

总结

职责链模式经常和组合模式一起使用,这样一个构件的父构件可以作为其继任者。

同时,DOM里的事件冒泡机制也和此好像有点类似,比如点击一个按钮以后,如果不阻止冒泡,其click事件将一直向父元素冒泡,利用这个机制也可以处理很多相关的问题,比如本系列设计模式享元模式里的《例1:事件集中管理》的示例代码。

Javascript 相关文章推荐
jquery中获取元素的几种方式小结
Jul 05 Javascript
Javascript中arguments对象详解
Oct 22 Javascript
jQuery前端框架easyui使用Dialog时bug处理
Dec 05 Javascript
SublimeText自带格式化代码功能之reindent
Dec 27 Javascript
JS中Array数组学习总结
Jan 18 Javascript
如何快速上手Vuex
Feb 14 Javascript
js 实现省市区三级联动菜单效果
Feb 20 Javascript
layer弹窗插件操作方法详解
May 19 Javascript
jquery中有哪些api jQuery主要API
Nov 20 jQuery
JS运动特效之任意值添加运动的方法分析
Jan 24 Javascript
webpack 动态批量加载文件的实现方法
Mar 19 Javascript
Javascript幻灯片播放功能实现过程解析
May 07 Javascript
教你如何使用firebug调试功能了解javascript闭包和this
Mar 04 #Javascript
深入理解JavaScript系列(37):设计模式之享元模式详解
Mar 04 #Javascript
jQuery插件开发的五种形态小结
Mar 04 #Javascript
深入理解JavaScript系列(36):设计模式之中介者模式详解
Mar 04 #Javascript
百度UEditor编辑器如何关闭抓取远程图片功能
Mar 03 #Javascript
jQuery实现复选框成对选择及对应取消的方法
Mar 03 #Javascript
js实现文本框中输入文字页面中div层同步获取文本框内容的方法
Mar 03 #Javascript
You might like
php 数组的合并、拆分、区别取值函数集
2010/02/15 PHP
PHP使用xmllint命令处理xml与html的方法
2014/12/15 PHP
如何使用jQuery+PHP+MySQL来实现一个在线测试项目
2015/04/26 PHP
PHP实现163邮箱自动发送邮件
2016/03/29 PHP
PHP 绘制网站登录首页图片验证码
2016/04/12 PHP
PHP性能测试工具xhprof安装与使用方法详解
2018/04/29 PHP
表单填写时用回车代替TAB的实现方法
2007/10/09 Javascript
jQuery动态添加的元素绑定事件处理函数代码
2011/08/02 Javascript
chrome不支持form.submit的解决方案
2015/04/28 Javascript
使用coffeescript编写node.js项目的方法汇总
2015/08/05 Javascript
举例讲解jQuery中可见性过滤选择器的使用
2016/04/18 Javascript
浅谈JavaScript中小数和大整数的精度丢失
2016/05/31 Javascript
使用JSON作为函数的参数的优缺点
2016/10/27 Javascript
jquery.Jcrop结合JAVA后台实现图片裁剪上传实例
2016/11/05 Javascript
js点击时关闭该范围下拉菜单之外的菜单方法
2018/01/11 Javascript
详解Chai.js断言库API中文文档
2018/01/31 Javascript
JavaScript设计模式之观察者模式实例详解
2019/01/16 Javascript
基于JS实现web端录音与播放功能
2019/04/17 Javascript
Angular中innerHTML标签的样式不起作用的原因解析
2019/06/18 Javascript
[47:36]Optic vs Newbee 2018国际邀请赛小组赛BO2 第二场 8.17
2018/08/18 DOTA
python实现斐波那契递归函数的方法
2014/09/08 Python
python队列通信:rabbitMQ的使用(实例讲解)
2017/12/22 Python
python实现dijkstra最短路由算法
2019/01/17 Python
解决Django Static内容不能加载显示的问题
2019/07/28 Python
python获取指定日期范围内的每一天,每个月,每季度的方法
2019/08/08 Python
Python实现手势识别
2020/10/21 Python
Alba Moda瑞士网上商店:独家意大利时尚女装销售
2016/11/28 全球购物
Ibatis如何使用动态表名
2015/07/12 面试题
名词解释型面试题(主要是网络)
2013/12/27 面试题
港湾网络笔试题
2014/04/19 面试题
公务员个人自我评价分享
2013/11/06 职场文书
搞笑婚庆主持词
2015/06/29 职场文书
同学聚会祝酒词
2015/08/10 职场文书
大学军训口号大全
2015/12/24 职场文书
Python利用FlashText算法实现替换字符串
2022/03/31 Python
python使用shell脚本创建kafka连接器
2022/04/29 Python