js职责链模式
职责链模式(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职责链模式的更多相关文章
- Js 职责链模式 简单理解
js 职责链模式 的简单理解.大叔的代码太高深了,不好理解. function Handler(s) { this.successor = s || null; this.handle = funct ...
- JS 职责链模式
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
- js 职责链模式简要介绍
定义: 使多个对象都有机会处理请求,避免发送者与接受者之间的耦合关系,将对象连成一条链,沿着这条链传递请求,直到有一个对象处理它. 如何把对象串联起来?解决方法通常是将另一个对象作为新创建对象的参数, ...
- JS常用的设计模式(15)—— 职责链模式
职责链模式是一个对象A向另一个对象B发起请求,如果B不处理,可以把请求转给C,如果C不处理,又可以把请求转给D.一直到有一个对象愿意处理这个请求为止. 打个比方,客户让老板写个php程序.老板肯定不写 ...
- js设计模式(12)---职责链模式
0.前言 老实讲,看设计模式真得很痛苦,一则阅读过的代码太少:二则从来或者从没意识到使用过这些东西.所以我采用了看书(<js设计模式>)和阅读博客(大叔.alloyteam.聂微东)相结合 ...
- JS设计模式(10)职责链模式(重要)
什么是职责链模式? 重要性:4 星,在项目中能对 if-else 语句进行优化 定义:避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到 ...
- js设计模式——6.模板方法模式与职责链模式
js设计模式——6.模板方法模式与职责链模式 职责链模式
- 5.js模式-职责链模式
1. 职责链模式 将对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止. var chain = function(fn){ this.fn = fn; this.successor = ...
- js原生设计模式——13桥接模式(相同业务逻辑抽象化处理的职责链模式)
桥接模式之多元化类之间的实例化调用实例 <!DOCTYPE html><html lang="en"><head> <meta ch ...
随机推荐
- 记录-Jquery uploadify文件上传实例
原本做的是from表单的文件上传,后来因需要用ajax异步,so接触到了Jquery uploadify上传 贴上代码,以供参考 需要引入的js文件 <link href="../re ...
- ThinkPHP的join方法
两张表: 表一:pre_company_member 关联字段:comp_id 表二:pre_company 关联字段:comp_id 查询这两表中的数据. 方法一:驼峰法 $member=M('C ...
- 约束、自定义异常、hashlib模块、logging日志模块
一.约束(重要***) 1.首先我们来说一下java和c#中的一些知识,学过java的人应该知道,java中除了有类和对象之外,还有接口类型,java规定,接口中不允许在方法内部写代码,只能约束继承它 ...
- 斯坦福大学Andrew Ng - 机器学习笔记(2) -- 逻辑回归 & 正则化
大概用了一个月,Andrew Ng老师的机器学习视频断断续续看完了,以下是个人学习笔记,入门级别,权当总结.笔记难免有遗漏和误解,欢迎讨论. 鸣谢:中国海洋大学黄海广博士提供课程视频和个人笔记,在此深 ...
- 2014牡丹江——Known Notation
题目链接 题意: 输入一个长度不超过1000的字符串,包含数字(1-9)和星号(*).字符串中的空格已经丢失,所以连起来的数字串能够看成很多分开的数.也能够看成连续的数,即能够随意加入空格. 如今有两 ...
- 获取JDBC响应做接口关联
1:从sql表中将需要取的数据查出来 2:我们需要把这个id为4451的数据从sql里面取出来,传到下一个sql里面,执行删除 3:写一个接口的传参有些不同,变成了var_id_1.var_id是之前 ...
- 003-搭建框架-实现IOC机制
一.实现目标 一种MVC[Model-View-Controller]一种设计模式,进行解耦. /* * 处理客户管理相关请求 */ @Controller public class Customer ...
- Android开发问题:ActivityNotFoundException: Unable to find explicit activity class
http://blog.csdn.net/debuglog/article/details/7236013 原因:AndroidManifest.xml未添加对应Activity配置. 解决办法:在A ...
- python删除列表中所有的空元素
while '' in list: list.remove('')
- SQL SERVER 2005 Express版, 精简版 下载
Microsoft SQL Server 2005 Express Edition(数据库) https://www.microsoft.com/zh-CN/download/details.as ...