职责链模式(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. MVC5学习系列

    前言 嗷~小弟我又出现了~咳咳..嚎过头了, 先说一说为什么写这个吧,~首先肯定是我自己需要学(废话 - -,)//,之前也写过MVC4的项目,嗯..但是仅限于使用并没有很深入的每个模块去了解, 这段 ...

  2. mock数据(模拟后台数据)

    mock数据(模拟后台数据) - Emily恩 - 博客园 https://www.cnblogs.com/enboke/p/vue.html Mock.js http://mockjs.com/ 前 ...

  3. 1.Configure the mongo Shell-官方文档摘录

    Customize the Prompt 自定义提示 You may modify the content of the prompt by setting the variable prompt i ...

  4. Django 请求生命周期【图示】

    Django 请求生命周期

  5. Python2 和 Python3 区别汇总

    [Python2 和 Python3 的区别汇总,不定期补充] print 在进行程序调试时用得最多的语句可能就是 print,在 Python 2 中,print 是一条语句,而 Python3 中 ...

  6. Jmeter关联技术

    JMeter:关联步骤 <1>录制成功,回放失败了: <2>录制两个业务相同的脚本,比对差别,找到动态数据,AptDiff_1.6.zip工具 <3>找到相应请求: ...

  7. win7与win server 2008防火墙设置

    转自:http://blog.51cto.com/jimshu/590411 Windows 防火墙通过阻止未授权用户通过 Internet 或网络访问您的计算机来帮助保护计算机. Windows 2 ...

  8. Version 1.5 of the JVM is not suitable for this product.Version:1.6 or greater is required

    近期在公司涉及到了服务器等的扩展,smartfoxserver扩展使用的Eclipse,尽管没学过java.可是咱毕竟是C++起价的,其它语言看看也就会了,项目依然做着,近期看到某同学有一些java的 ...

  9. 003-搭建框架-实现IOC机制

    一.实现目标 一种MVC[Model-View-Controller]一种设计模式,进行解耦. /* * 处理客户管理相关请求 */ @Controller public class Customer ...

  10. 剑指offer 面试33题

    面试33题:题:二叉搜索树的后序遍历序列 题目:输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果.如果是则输出Yes,否则输出No.假设输入的数组的任意两个数字都互不相同. 解题思路:递 ...