职责链模式(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 = ;
jinli.RequestApplications(request); Request request2 = new Request();
request2.RequestType = "请假";
request2.RequestContent = "小菜请假";
request2.Number = ;
jinli.RequestApplications(request2); Request request3 = new Request();
request3.RequestType = "加薪";
request3.RequestContent = "小菜请求加薪";
request3.Number = ;
jinli.RequestApplications(request3); Request request4 = new Request();
request4.RequestType = "加薪";
request4.RequestContent = "小菜请求加薪";
request4.Number = ;
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 <= )
{
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 <= )
{
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 <= )
{
Console.WriteLine("{0}:{1} 数量{2} 被批准", name, request.RequestContent, request.Number);
}
else if (request.RequestType == "加薪" && request.Number > )
{
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 = '<object type="application/x-shockwave-flash"></object>';
return $(str).appendTo($('body'));
}
return 'nextSuccessor';
}; var getFormUploadObj = function(){
return $('<form><input name="file" type="file"/></form>').appendTo($('body'));
}; var getUploadObj = getActiveUploadObj.after(getFlashUploadObj).after(getFormUploadObj);
console.log(getUploadObj());

js职责链模式的更多相关文章

  1. Js 职责链模式 简单理解

    js 职责链模式 的简单理解.大叔的代码太高深了,不好理解. function Handler(s) { this.successor = s || null; this.handle = funct ...

  2. JS 职责链模式

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  3. js 职责链模式简要介绍

    定义: 使多个对象都有机会处理请求,避免发送者与接受者之间的耦合关系,将对象连成一条链,沿着这条链传递请求,直到有一个对象处理它. 如何把对象串联起来?解决方法通常是将另一个对象作为新创建对象的参数, ...

  4. JS常用的设计模式(15)—— 职责链模式

    职责链模式是一个对象A向另一个对象B发起请求,如果B不处理,可以把请求转给C,如果C不处理,又可以把请求转给D.一直到有一个对象愿意处理这个请求为止. 打个比方,客户让老板写个php程序.老板肯定不写 ...

  5. js设计模式(12)---职责链模式

    0.前言 老实讲,看设计模式真得很痛苦,一则阅读过的代码太少:二则从来或者从没意识到使用过这些东西.所以我采用了看书(<js设计模式>)和阅读博客(大叔.alloyteam.聂微东)相结合 ...

  6. JS设计模式(10)职责链模式(重要)

    什么是职责链模式? 重要性:4 星,在项目中能对 if-else 语句进行优化 定义:避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到 ...

  7. js设计模式——6.模板方法模式与职责链模式

    js设计模式——6.模板方法模式与职责链模式 职责链模式

  8. 5.js模式-职责链模式

    1. 职责链模式 将对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止. var chain = function(fn){ this.fn = fn; this.successor = ...

  9. js原生设计模式——13桥接模式(相同业务逻辑抽象化处理的职责链模式)

    桥接模式之多元化类之间的实例化调用实例 <!DOCTYPE html><html lang="en"><head>    <meta ch ...

随机推荐

  1. Dictionary——通过value找Key

    Dictionary<string, string> dic = GetRoleDescriptions(); string key = dic.FirstOrDefault(x => ...

  2. On the fly test

    on the fly test就是边开发边测试的意思.test code不是早就生成好的,在一边生成code的同时一边做test running,最大的好处是,flexiable ,每一次可以选择不同 ...

  3. extract

    w http://php.net/manual/en/function.extract.php <?php /* Suppose that $var_array is an array retu ...

  4. 解决IIS部署网站引用woff/woff2/svg字体报404错误

    一.问题 在IIS上部署网站,网页引用woff字体时,浏览器报“找不到woff.woff2字体”.“404”错误,不仅预设的字体加载不出来,还影响网页加载速度. 二.原因 IIS默认设置情况下不识别. ...

  5. python多进程编程(一)

    multiprocessing模块介绍 python中的多线程无法利用多核优势,如果想要充分地使用多核CPU的资源(os.cpu_count()查看),在python中大部分情况需要使用多进程.Pyt ...

  6. 我的Android进阶之旅------>android中service的onStartCommand()方法中intent为null的问题

    今天在维护公司的一个APP的时候,突然爆了空指针异常, Caused by: java.lang.NullPointerException: Attempt to invoke virtual met ...

  7. F-02 创建财务凭证BAPI

    **.获取抬头参数, documentheader **.项目参数 accountgl = lt_acgl"G/L account item accountreceivable = lt_a ...

  8. Log4j详细配置解释

    原文地址:https://www.cnblogs.com/godtrue/p/6444158.html log4j(七)——log4j.xml简单配置样例说明 一:测试环境与log4j(一)——为什么 ...

  9. 面试题2:实现Singleton模式(Java实现)

    SIngleton(单例)设计模式 它是最简单的常用的设计模式之一,设计模式在面向对象程序设计中起着举足轻重的作用,Singleton是唯一一个能够用短短几十行代码完整实现的模式. public cl ...

  10. 建议41:使用argparse处理命令行参数

    # -*- coding:utf-8 -*- ''' 以现阶段最好用的参数处理标准库是argparse ''' import argparse parser = argparse.ArgumentPa ...