深入理解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的trim,ltrim,rtrim自定义函数
Sep 21 Javascript
利用JQuery的load函数动态加载其它页面的内容的实现代码
Dec 14 Javascript
js获得参数的getParameter使用示例
Feb 26 Javascript
JavaScript实现查找字符串中第一个不重复的字符
Dec 29 Javascript
jQuery实现模拟flash头像裁切上传功能示例
Dec 11 Javascript
js控制按钮,防止频繁点击响应的实例
Feb 15 Javascript
BootStrap表单控件之文本域textarea
May 23 Javascript
JS+CSS实现滚动数字时钟效果
Dec 25 Javascript
vue2.0实现前端星星评分功能组件实例代码
Feb 12 Javascript
React+Webpack快速上手指南(小结)
Aug 15 Javascript
JavaScript使用indexOf()实现数组去重的方法分析
Sep 04 Javascript
微信小程序实现身份证取景框拍摄
Sep 09 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
深入分析使用mysql_fetch_object()以对象的形式返回查询结果
2013/06/05 PHP
ThinkPHP之M方法实例详解
2014/06/20 PHP
php检查日期函数checkdate用法实例
2015/03/19 PHP
PHP filter_var() 函数, 验证判断EMAIL,URL等
2021/03/09 PHP
xml 封装与解析(javascript和C#中)
2009/07/26 Javascript
小议javascript 设计模式 推荐
2009/10/28 Javascript
JavaScript代码简单实现求杨辉三角给定行的最大值
2013/10/29 Javascript
每个 JavaScript 工程师都应懂的33个概念
2018/10/22 Javascript
jquery操作select常见方法大全【7种情况】
2019/05/28 jQuery
JS字符串与二进制的相互转化实例代码详解
2019/06/28 Javascript
在Vue环境下利用worker运行interval计时器的步骤
2019/08/01 Javascript
vue.js的状态管理vuex中store的使用详解
2019/11/08 Javascript
[03:58]兄弟们,回来开黑了!DOTA2昔日战友招募宣传视频
2016/07/17 DOTA
[04:40]2016国际邀请赛中国区预选赛全程TOP10镜头集锦
2016/07/01 DOTA
详解MySQL数据类型int(M)中M的含义
2016/11/20 Python
Python 爬取必应壁纸的实例讲解
2020/02/24 Python
在python中实现求输出1-3+5-7+9-......101的和
2020/04/02 Python
python 实现分组求和与分组累加求和代码
2020/05/18 Python
Django多层嵌套ManyToMany字段ORM操作详解
2020/05/19 Python
python zip()函数的使用示例
2020/09/23 Python
matplotlib 范围选区(SpanSelector)的使用
2021/02/24 Python
CSS3美化表单控件全集
2016/06/29 HTML / CSS
HTML5之HTML元素扩展(上)—新增加的元素及使用概述
2013/01/31 HTML / CSS
小程序瀑布流解决左右两边高度差距过大的问题
2019/02/20 HTML / CSS
英国书籍、CD、DVD和游戏的第一道德零售商:Awesome Books
2020/02/22 全球购物
DIY手工制作经营店创业计划书
2014/02/01 职场文书
硕士研究生求职自荐信范文
2014/03/11 职场文书
管理工程专业求职信
2014/08/10 职场文书
班子四风对照检查材料
2014/08/21 职场文书
机关干部四风问题自我剖析及整改措施
2014/10/26 职场文书
2014年企业员工工作总结
2014/12/09 职场文书
工厂门卫岗位职责
2015/04/13 职场文书
三八妇女节新闻稿
2015/07/17 职场文书
如何利用golang运用mysql数据库
2022/03/13 Golang
vue router 动态路由清除方式
2022/05/25 Vue.js
Python中的 No Module named ***问题及解决
2022/07/23 Python