深入理解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 相关文章推荐
js 获取浏览器高度和宽度值(多浏览器)
Sep 02 Javascript
javascript 操作Word和Excel的实现代码
Oct 26 Javascript
ExtJS 入门
Oct 29 Javascript
JS操作XML实例总结(加载与解析XML文件、字符串)
Dec 08 Javascript
js获取鼠标点击的对象,点击另一个按钮删除该对象的实现代码
May 13 Javascript
js从外部获取图片的实现方法
Aug 05 Javascript
AngularJS实现页面定时刷新
Mar 14 Javascript
JS中的数组转变成JSON格式字符串的方法
May 09 Javascript
JS判断数组里是否有重复元素的方法小结
May 21 Javascript
微信小程序 调用远程接口 给全局数组赋值代码实例
Aug 13 Javascript
Layui 导航默认展开和菜单栏选中高亮设置的方法
Sep 04 Javascript
js实现移动端吸顶效果
Jan 08 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开发过程中常用函数收藏
2009/12/14 PHP
解析web文件操作常见安全漏洞(目录、文件名检测漏洞)
2013/06/29 PHP
用PHP做了一个领取优惠券活动的示例代码
2019/07/05 PHP
PHP设计模式(一)工厂模式Factory实例详解【创建型】
2020/05/02 PHP
20款非常优秀的 jQuery 工具提示插件 推荐
2012/07/15 Javascript
JS获取当前网址、主机地址项目根路径
2013/11/19 Javascript
js给页面加style无效果的解决方法
2014/01/20 Javascript
js charAt的使用示例
2014/02/18 Javascript
javascript移动设备Web开发中对touch事件的封装实例
2014/06/05 Javascript
JS实现的生成随机数的4个函数分享
2015/02/11 Javascript
果断收藏9个Javascript代码高亮脚本
2016/01/06 Javascript
jQuery获取父元素节点、子元素节点及兄弟元素节点的方法
2016/04/14 Javascript
jquery自动补齐功能插件flexselect用法示例
2016/08/06 Javascript
jQuery实现的tab标签切换效果示例
2016/09/05 Javascript
BootStrap tooltip提示框使用小结
2016/10/26 Javascript
webpack4.x打包过程详解
2018/07/18 Javascript
NodeJs生成sitemap站点地图的方法示例
2019/06/11 NodeJs
详解如何在Vue项目中发送jsonp请求
2019/10/25 Javascript
vue 强制组件重新渲染(重置)的两种方案
2019/10/29 Javascript
vue中是怎样监听数组变化的
2020/10/24 Javascript
Vue通过阿里云oss的url连接直接下载文件并修改文件名的方法
2020/12/25 Vue.js
[00:14]护身甲盾
2019/03/06 DOTA
Django Highcharts制作图表
2016/08/27 Python
python机器人行走步数问题的解决
2018/01/29 Python
对python 各种删除文件失败的处理方式分享
2018/04/24 Python
PyQt5显示GIF图片的方法
2019/06/17 Python
Django ForeignKey与数据库的FOREIGN KEY约束详解
2020/05/20 Python
Python selenium爬虫实现定时任务过程解析
2020/06/08 Python
虚拟环境及venv和virtualenv的区别说明
2021/02/05 Python
CSS3属性选择符介绍
2008/10/17 HTML / CSS
应届生文秘专业个人自荐信格式
2013/09/21 职场文书
顶岗实习计划书
2014/01/10 职场文书
教师辞职报告范文
2014/01/20 职场文书
怎样写辞职信
2015/02/27 职场文书
学校教师培训工作总结
2015/10/14 职场文书
详解java如何集成swagger组件
2021/06/21 Java/Android