博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
js职责链模式
阅读量:6515 次
发布时间:2019-06-24

本文共 8407 字,大约阅读时间需要 28 分钟。

职责链模式(Chain of Responsiblity),使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。

从责任链模式的定义可以发现,责任链模式涉及的对象只有处理者角色,但由于有多个处理者,它们具有共同的处理请求的方法,所以这里抽象出一个抽象处理者角色进行代码复用。这样分析下来,责任链模式的结构图也就不言而喻了,具体结构图如下所示。

主要涉及两个角色:

  • 抽象处理者角色(Handler):定义出一个处理请求的接口。这个接口通常由接口或抽象类来实现。
  • 具体处理者角色(ConcreteHandler):具体处理者接受到请求后,可以选择将该请求处理掉,或者将请求传给下一个处理者。因此,每个具体处理者需要保存下一个处理者的引用,以便把请求传递下去。

 

在以下场景中可以考虑使用责任链模式:

  • 一个系统的审批需要多个对象才能完成处理的情况下,例如请假系统等。
  • 代码中存在多个if-else语句的情况下,此时可以考虑使用责任链模式来对代码进行重构。

 

C#职责链模式:

namespace 职责链模式{    class Program    {        static void Main(string[] args)        {            CommonManager jinli = new CommonManager("金利");            Majordomo zongjian = new Majordomo("宗剑");            GeneralManager zhongjingli = new GeneralManager("钟精励");            jinli.SetSuperior(zongjian);            zongjian.SetSuperior(zhongjingli);            Request request = new Request();            request.RequestType = "请假";            request.RequestContent = "小菜请假";            request.Number = 1;            jinli.RequestApplications(request);            Request request2 = new Request();            request2.RequestType = "请假";            request2.RequestContent = "小菜请假";            request2.Number = 4;            jinli.RequestApplications(request2);            Request request3 = new Request();            request3.RequestType = "加薪";            request3.RequestContent = "小菜请求加薪";            request3.Number = 500;            jinli.RequestApplications(request3);            Request request4 = new Request();            request4.RequestType = "加薪";            request4.RequestContent = "小菜请求加薪";            request4.Number = 1000;            jinli.RequestApplications(request4);            Console.Read();        }    }    //管理者    abstract class Manager    {        protected string name;        //管理者的上级        protected Manager superior;        public Manager(string name)        {            this.name = name;        }        //设置管理者的上级        public void SetSuperior(Manager superior)        {            this.superior = superior;        }        //申请请求        abstract public void RequestApplications(Request request);    }    //经理    class CommonManager : Manager    {        public CommonManager(string name)            : base(name)        { }        public override void RequestApplications(Request request)        {            if (request.RequestType == "请假" && request.Number <= 2)            {                Console.WriteLine("{0}:{1} 数量{2} 被批准", name, request.RequestContent, request.Number);            }            else            {                if (superior != null)                    superior.RequestApplications(request);            }        }    }    //总监    class Majordomo : Manager    {        public Majordomo(string name)            : base(name)        { }        public override void RequestApplications(Request request)        {            if (request.RequestType == "请假" && request.Number <= 5)            {                Console.WriteLine("{0}:{1} 数量{2} 被批准", name, request.RequestContent, request.Number);            }            else            {                if (superior != null)                    superior.RequestApplications(request);            }        }    }    //总经理    class GeneralManager : Manager    {        public GeneralManager(string name)            : base(name)        { }        public override void RequestApplications(Request request)        {            if (request.RequestType == "请假")            {                Console.WriteLine("{0}:{1} 数量{2} 被批准", name, request.RequestContent, request.Number);            }            else if (request.RequestType == "加薪" && request.Number <= 500)            {                Console.WriteLine("{0}:{1} 数量{2} 被批准", name, request.RequestContent, request.Number);            }            else if (request.RequestType == "加薪" && request.Number > 500)            {                Console.WriteLine("{0}:{1} 数量{2} 再说吧", name, request.RequestContent, request.Number);            }        }    }    //申请    class Request    {        //申请类别        private string requestType;        public string RequestType        {            get { return requestType; }            set { requestType = value; }        }        //申请内容        private string requestContent;        public string RequestContent        {            get { return requestContent; }            set { requestContent = value; }        }        //数量        private int number;        public int Number        {            get { return number; }            set { number = value; }        }    }}

 

灵活可拆分的职责链节点

首先需要改写一下分别表示3种购买模式的节点函数,我们约定,如果某个节点不能处理请求,则返回一个特定的字符串'nextSuccessor'来表示该请求需要继续往后面传递:

 

var order500 = function(orderType,pay,stock){    if(orderType === 1 && pay === true){        console.log('500元定金预购,得到100优惠券');    }else{        return 'nextSucessor';    //我不知道下一个节点是谁,反正把请求往后面传递    }};var order200 = function(orderType,pay,stock){    if(orderType === 2 && pay === true){        console.log('200元定金预购,得到50优惠券');    }else{        return 'nextSucessor';    //我不知道下一个节点是谁,反正把请求往后面传递    }};var orderNormal = function(orderType,pay,stock){    if(stock > 0){        console.log('普通购买,无优惠券');    }else{        console.log('手机库存不足');    }};

 

接下来需要把函数包装进职责链节点,我们定义一个构造函数Chain,在new Chain的时候传递参数即为需要被包装的函数,同时它拥有一个实例属性this.sucessor,表示在链中的下一个节点。

此外Chain的prototype中还有两个函数,它们的作用如下所示:

//Chain.prototype.setNextSucessor   指定在链中的下一个节点//Chain.prototype.passRequest   传递请求给某个节点var Chain = function(fn){    this.fn = fn;    this.successor = null;};Chain.prototype.setNextSuccessor = function(successor){    return this.successor = successor;};Chain.prototype.passRequest = function(){    var ret = this.fn.apply(this,arguments);        if(ret === 'nextSuccessor'){        return this.successor && this.successor.passRequest.apply(this.successor,arguments);    }        return ret;};

现在我们把3个订单函数分别包装成职责链的节点:

var chainOrder500 = new Chain(order500);var chainOrder200 = new Chain(order200);var chainOrderNormal = new Chain(orderNormal);

然后指定节点在职责链中的顺序:

chainOrder500.setNextSuccessor(chainOrder200);chainOrder200.setNextSuccessor(chainOrderNormal);

最后把请求传递给第一个节点:

chainOrder500.passRequest(1,true,500);    //输出:500元定金预购,得到100优惠券chainOrder500.passRequest(2,true,500);    //输出:200元定金预购,得到50优惠券chainOrder500.passRequest(3,true,500);    //输出:普通购买,无优惠券chainOrder500.passRequest(1,false,0);    //输出:手机库存不足

通过改进,我们可以自由灵活地增加、移除和修改链中的节点顺序,假如某天网站运营人员又想出了支持300元定金购买,那我们就在该链中增加一个节点即可:

var order300 = function(){    //具体实现略};chainOrder300 = new Chain(order300);chainOrder500.setNextSuccessor(chainOrder300);chainOrder300.setNextSuccessor(chainOrder200);

对于程序员来说,我们总是喜欢去改动那些相对容易改动的地方,就像改动框架的配置文件远比改动框架的源代码简单得多。在这里完全不用理会原来的订单函数代码,我们要做的只是增加一个节点,然后重新设置链中的相关节点的顺序。

异步的职责链

 

var fn1 = new Chain(function(){    console.log(1);    return 'nextSuccessor';});var fn2 = new Chain(function(){    console.log(2);    var self = this;    setTimeout(function(){        self.next();    },1000);});var fn3 = new Chain(function(){    console.log(3);});fn1.setNextSuccessor(fn2).setNextSuccessor(fn3);fn1.passRequest();

 

职责链模式并非没有弊端,如果请求得不到答复,径直从链尾离开,或者抛出异常。在这种情况下,我们可以在链尾增加一个保底的接受者节点来处理这种即将离开链尾的请求。

另外,职责链模式使得程序中多了一些节点对象,可能在某一次的请求传递过程中,大部分节点并没有起到实质性的作用,它们的作用仅仅是让请求传递下去,从性能方面考虑,我们要避免长的职责链带来的性能损耗。

 

用AOP实现职责链

 
下 面我们改写一下Function.prototype.after函数,使得第一个函数返回'nextSuccessor'时,将请求继续传递给下一个函 数,无论是返回字符串'nextSuccessor'或者fase都只是一个约定,当然在这里我们也可以让函数返回false表示传递请求,选择 'nextSuccessor'字符串是因为它看起来更能表达我们的目的,代码如下:

 

Function.prototype.after = function(fn){    var self = this;    return function(){        var ret = self.apply(this,arguments);        if(ret === 'nextSuccessor'){            return fn.apply(this,arguments);        }        return ret;    }};var order = order500yuan.after(order200yuan).after(orderNormal);order(1,true,500);   //输出:500元定金预购,得到100优惠券order(2,true,500);   //输出:200元定金预购,得到50优惠券order(1,false,500);  //输出:普通购买,无优惠券

 

 

用职责链模式获取文件上传对象

 

var getActiveUploadObj = function(){    try{        return new ActiveXObject("TXFTNActiveX.FTNUpload");   //IE上传控件    }catch(e){        return 'nextSuccessor';    }};var getFlashUploadObj = function(){    if(supportFlash()){        var str = '';        return $(str).appendTo($('body'));    }    return 'nextSuccessor';};var getFormUploadObj = function(){    return $('
').appendTo($('body'));};var getUploadObj = getActiveUploadObj.after(getFlashUploadObj).after(getFormUploadObj);console.log(getUploadObj());

 

转载于:https://www.cnblogs.com/gongshunkai/p/6635290.html

你可能感兴趣的文章
git compare for docx file
查看>>
ulimit 设置 最大 打开文件数(祝大家 未来 精彩无限 -- unlimited)
查看>>
局域网内部署 Docker Registry
查看>>
小程序
查看>>
十四、df命令、du命令、磁盘分区
查看>>
子网划分
查看>>
ETL工具kettle的使用,基础--1 (最基本的输入输出)
查看>>
决心书
查看>>
Confluence 6 删除垃圾内容
查看>>
手机控必备网站,让自己拥有更好的智能手机
查看>>
如何在PDF上添加水印
查看>>
淺談比特币
查看>>
2.Linux基本知识点整理(不定时更新中..)
查看>>
所有Mac用户都需要知道的9个实用终端命令行
查看>>
使用ISO镜像构建基于FTP,HTTP的YUM源服务器
查看>>
理解 OpenStack 高可用(HA)(3):Neutron 分布式虚拟路由(Neutron Distributed Virtual Routing)...
查看>>
window 让普通用户有重启服务权限方法
查看>>
用Ubuntu和RStudio Server搭建一个R语言的云平台
查看>>
解决Linux无线网卡灯 疯狂闪烁的问题
查看>>
三星AI技术成功让蒙娜莉萨有多种表情
查看>>