深入理解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 相关文章推荐
JAVASCRIPT IE 与 FF中兼容问题小结
Feb 18 Javascript
ie浏览器使用js导出网页到excel并打印
Mar 11 Javascript
Javascript的严格模式strict mode详细介绍
Jun 06 Javascript
浅析script标签中的defer与async属性
Nov 30 Javascript
javascript动画系列之模拟滚动条
Dec 13 Javascript
微信小程序 本地存储及登录页面处理实例详解
Jan 11 Javascript
jQuery Masonry瀑布流插件使用方法详解
Jan 18 Javascript
详解webpack进阶之loader篇
Aug 23 Javascript
Vue keepAlive 数据缓存工具实现返回上一个页面浏览的位置
May 10 Javascript
vue中axios实现数据交互与跨域问题
May 12 Javascript
vue element-ui之怎么封装一个自己的组件的详解
May 20 Javascript
js的新生代垃圾回收知识点总结
Aug 22 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/08/08 PHP
PHP小教程之实现双向链表
2014/06/12 PHP
我整理的PHP 7.0主要新特性
2016/01/07 PHP
php resizeimage 部分jpg文件 生成缩略图失败的原因分析及解决办法
2016/03/23 PHP
php如何实现不借助IDE快速定位行数或者方法定义的文件和位置
2017/01/17 PHP
php实现微信支付之退款功能
2018/05/30 PHP
10款新鲜出炉的 jQuery 插件(Ajax 插件,有幻灯片、图片画廊、菜单等)
2011/06/08 Javascript
浅谈javascript 归并方法
2015/01/21 Javascript
js判断浏览器类型及设备(移动页面开发)
2015/07/30 Javascript
JS常见DOM节点操作示例【创建 ,插入,删除,复制,查找】
2018/05/14 Javascript
node.js之基础加密算法模块crypto详解
2018/09/11 Javascript
Handtrack.js库实现实时监测手部运动(推荐)
2021/02/08 Javascript
python模拟登录百度贴吧(百度贴吧登录)实例
2013/12/18 Python
Python的字典和列表的使用中一些需要注意的地方
2015/04/24 Python
Python 25行代码实现的RSA算法详解
2018/04/10 Python
python解决字符串倒序输出的问题
2018/06/25 Python
python使用response.read()接收json数据的实例
2018/12/19 Python
python多线程并发让两个LED同时亮的方法
2019/02/18 Python
python从list列表中选出一个数和其对应的坐标方法
2019/07/20 Python
利用Python小工具实现3秒钟将视频转换为音频
2019/10/29 Python
简单了解python数组的基本操作
2019/11/26 Python
使用Python制作缩放自如的圣诞老人(圣诞树)
2019/12/25 Python
pytorch GAN伪造手写体mnist数据集方式
2020/01/10 Python
将pytorch转成longtensor的简单方法
2020/02/18 Python
Pytorch使用PIL和Numpy将单张图片转为Pytorch张量方式
2020/05/25 Python
python 爬取腾讯视频评论的实现步骤
2021/02/18 Python
CSS3弹性盒模型开发笔记(二)
2016/04/26 HTML / CSS
西海岸男士和男童服装:Johnnie-O
2018/03/15 全球购物
N.Peal官网:来自伦敦的高档羊绒品牌
2018/10/29 全球购物
西班牙高科技产品购物网站:MejorDeseo
2019/09/08 全球购物
试用期转正员工自我评价
2014/09/18 职场文书
2016年习主席讲话学习心得体会
2016/01/20 职场文书
《中彩那天》教学反思
2016/02/24 职场文书
导游词之南京莫愁湖公园
2019/11/13 职场文书
css3属性选择器 “~”(波浪号) “,”(逗号) “+”(加号)和 “>”(大于号)
2022/04/19 HTML / CSS
pnpm对npm及yarn降维打击详解
2022/08/05 Javascript