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

介绍

职责链模式(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:事件集中管理》的示例代码。

(0)

相关推荐

  • C#职责链模式实例详解

    本文实例讲述了C#职责链模式.分享给大家供大家参考.具体如下: ConcreteHandler1.cs如下: using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConsoleApplication1 { public class ConcreteHandler1:Handler { public override void HandRequest(int

  • php设计模式 Chain Of Responsibility (职责链模式)

    复制代码 代码如下: <?php /** * 职责链模式 * * 为解除请求的发送者和接收者之间的耦合,而使用多个对象都用机会处理这个请求,将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它 * */ abstract class Handler { protected $_handler = null; public function setSuccessor($handler) { $this->_handler = $handler; } abstract functio

  • 轻松掌握JavaScript享元模式

    在JavaScript中,浏览器特别是移动端的浏览器分配的内存很有限,如何节省内存就成了一件非常有意义的事情.节省内存的一个有效方法是减少对象的数量. 享元模式(Flyweight),运行共享技术有效地支持大量细粒度的对象,避免大量拥有相同内容的小类的开销(如耗费内存),使大家共享一个类(元类). 享元模式可以避免大量非常相似类的开销,在程序设计中,有时需要生产大量细粒度的类实例来表示数据,如果能发现这些实例除了几个参数以外,开销基本相同的话,就可以大幅度较少需要实例化的类的数量.如果能把那些参

  • 轻松掌握JavaScript中介者模式

    中介者模式的作用就是解除对象与对象之间的紧耦合关系,它也称'调停者'.所有的对象都通过中介者对象来通信,而不是相互引用,所以当一个对象发生改变时,只需要通知中介者即可. 如:机场的指挥塔,每架飞机都只需要和指挥塔通信即可,指挥塔知道每架飞机的飞行状况,可以安排所有起降时间,调整航线等 中介者模式符合迪米特法则,即最少知识原则,指一个对象应该尽可能少地了解另外的对象.如果对象之间的耦合性太高,则改变一个对象,会牵动很多对象,难于维护.当对象耦合很紧时,要修改一个对象而不影响其它的对象是很困难的.

  • C++设计模式之职责链模式

    前言 最近心情很差,因为生活,因为工作:所以想请几天假去丽江玩玩.就向项目经理提交了休假申请,我的项目经理向项目主管提交了我的休假申请,项目主管向部门经理提交了我的休假申请:最后,部门经理同意了我的休假申请.是的,一个简单的休假申请,需要这么复杂的流程,这也是一个公司保证它正常运行的必要.如果部门经理休假了,那么我的休假申请由谁审批呢?这个时候由项目主管代替部门经理进行审批.一个休假申请的审批制度有着严格的要求.而在处理这个请假审批时,各个人员就好比在一条链上的节点,我不知道我的请求由谁审批,但

  • 轻松掌握JavaScript单例模式

    定义:保证一个对象(类)仅有一个实例,并提供一个访问它的全局访问点: 实现原理:利用闭包来保持对一个局部变量的引用,这个变量保存着首次创建的唯一的实例;  主要用于:全局缓存.登录浮窗等只需要唯一一个实例的时候: 一. 为一个非单例模式对象(如:Demo)实现单例模式的方法:  给Demo添加一个静态方法来实现单例: Demo.getSingle = (function(){ var demo = null; return function(name){ if(!demo){ demo = ne

  • 轻松掌握JavaScript装饰者模式

    在传统的面向对象语言中,给对象添加功能常常使用继承的方式,但继承的方式会带来问题:当父类改变时,他的所有子类都将随之改变. 当JavaScript脚本运行时,在一个对象中(或他的原型上)增加行为会影响该对象的所有实例, 装饰者是一种实现继承的替代方案,它通过重载方法的形式添加新功能,该模式可以在被装饰者前面(before)或者后面(after)加上自己的行为以达到特定的目的. 装饰者模式是为已有功能动态地添加更多功能的一种方式,把每个要装饰的功能放在单独的函数里,然后用该函数包装所要装饰的已有函

  • JavaScript职责链模式概述

    一.概述 职责链模式(Chain of responsibility),就是使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系.将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理他为止. 貌似和数据结构中的链表一样. 但,不要搞混了,职责链可不等于链表哦,因为职责链可以在任何一个节点开始往下查找,而链表,则必须从头结点开始往下查找. 比如,DOM事件机制中的冒泡事件就属于职责链,而捕获事件则属于链表. 二.利用职责链模拟冒泡 假设我们有三个对象:li.ul.di

  • 轻松掌握JavaScript策略模式

    定义:定义一系列的算法,把它们一个个封装成函数,也可把它们作为属性统一封装进一个对象,然后再定义一个方法,该方法可根据参数自动选择执行对应的算法. 一般用于在实现一个功能时,有很多个方案可选择的情况. 例子1:根据员工薪水salary.绩效等级S.A.B.C,来计算年终奖 //封装了所有算法的策略对象 var strategies = { 'S': function(salary){ return salary*4; }, 'A': function(salary){ return salary

  • 轻松掌握JavaScript代理模式

    在面向对象设计中,有一个单一职责原则,指就一个类(对象.函数)而言,应该仅有一个引起它变化的原因.如果一个对象承担了过多的职责,就意味着它将变得巨大,引起它变化的原因就多,它把这些职责耦合到了一起,这种耦合会导致程序难于维护和重构. 这时候,我们可以把该对象(本体)的其中一部分职责分离出来给一些第三方对象去做,本体只管自己的一些核心职责,这些第三方对象就称作代理.代理对象可以作为对象(也叫"真正的主体")的保护者,让真正的主体对象做尽量少的工作.在代理设计模式中,一个对象充当了另一个对

随机推荐