深入理解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 相关文章推荐
HTTP 304错误的详细讲解
Nov 13 Javascript
js动态添加的DIV中的onclick事件简单实例
Jul 25 Javascript
jquery.zclip轻量级复制失效问题
Jan 08 Javascript
分分钟玩转Vue.js组件(二)
Mar 01 Javascript
详解vue事件对象、冒泡、阻止默认行为
Mar 20 Javascript
js导出Excel表格超出26位英文字符的解决方法ES6
Nov 15 Javascript
p5.js入门教程之鼠标交互的示例
Mar 16 Javascript
Vue项目使用CDN优化首屏加载问题
Apr 01 Javascript
原生js基于canvas实现一个简单的前端截图工具代码实例
Sep 10 Javascript
JavaScript 正则应用详解【模式、欲查、反向引用等】
May 13 Javascript
wepy--用vantUI 实现上弹列表并选择相应的值操作
Nov 03 Javascript
vue项目中使用rem,在入口文件添加内容操作
Nov 11 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
神族 Protoss 历史背景
2020/03/14 星际争霸
PHP获取一年中每个星期的开始和结束日期的方法
2015/02/12 PHP
Zend Framework路由器用法实例详解
2016/12/11 PHP
权威JavaScript 中的内存泄露模式
2007/08/13 Javascript
jquery load()在firefox(火狐)下显示不正常的解决方法
2011/04/05 Javascript
html中使用javascript调用本地程序(exe、doc等)实现代码
2013/04/26 Javascript
将form表单中的元素转换成对象的方法适用表单提交
2014/05/02 Javascript
js从Cookies里面取值的简单实现
2014/06/30 Javascript
jQuery实现的个性化返回底部与返回顶部特效代码
2015/10/30 Javascript
jQuery实现二级下拉菜单效果
2016/01/05 Javascript
jquery判断input值不为空的方法
2016/06/05 Javascript
angular框架实现全选与单选chekbox的自定义
2017/07/06 Javascript
详解Vue.js Mixins 混入使用
2017/09/15 Javascript
React-Native之定时器Timer的实现代码
2017/10/04 Javascript
浅谈React和Redux的连接react-redux
2017/12/04 Javascript
vue 不使用select实现下拉框功能(推荐)
2018/05/17 Javascript
vue中设置height:100%无效的问题及解决方法
2018/07/27 Javascript
vue实现图片预览组件封装与使用
2019/07/13 Javascript
JS中如何轻松遍历对象属性的方式总结
2019/08/06 Javascript
layui动态加载多表头的实例
2019/09/05 Javascript
解决echarts中横坐标值显示不全(自动隐藏)问题
2020/07/20 Javascript
js实现简单选项卡制作
2020/08/05 Javascript
[47:43]Alliance vs KG 2019国际邀请赛小组赛 BO2 第一场 8.16
2019/08/18 DOTA
Python设计模式编程中Adapter适配器模式的使用实例
2016/03/02 Python
Python3连接MySQL(pymysql)模拟转账实现代码
2016/05/24 Python
Python如何判断数独是否合法
2016/09/08 Python
Python使用正则表达式抓取网页图片的方法示例
2017/04/21 Python
python在文本开头插入一行的实例
2018/05/02 Python
python selenium自动上传有赞单号的操作方法
2018/07/05 Python
python如何通过twisted搭建socket服务
2020/02/03 Python
使用python执行shell脚本 并动态传参 及subprocess的使用详解
2020/03/06 Python
python中random.randint和random.randrange的区别详解
2020/09/20 Python
澳大利亚在线性感内衣商店:Fantasy Lingerie
2021/02/07 全球购物
求职信:会计求职的写作技巧
2019/04/24 职场文书
《现实主义勇者的王国再建记》第三弹OST全曲试听片段公开
2022/04/04 日漫
Win11电脑显示本地时间与服务器时间不一致怎么解决?
2022/04/05 数码科技