职责链模式的定义是:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系,将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
也就是说,请求以后,从第一个对象开始,链中收到请求的对象要么亲自处理它,要么转发给链中的下一个候选者。提交请求的对象并不明确知道哪一个对象将会处理它——也就是该请求有一个隐式的接受者(implicit receiver)。根据运行时刻,任一候选者都可以响应相应的请求,候选者的数目是任意的,你可以在运行时刻决定哪些候选者参与到链中。
职责链模式的名字非常形象,一系列可能会处理请求的对象被该连接成一条链,请求在这些对象之间依次传递,直到遇到一个可以处理它的对象,我们把这些对象成为链中的节点。
1、举例说明现实中的职责链模式
职责链模式的例子在现实生活中很多,下面举2个例子说明相关的场景。
1) 你去政府部门求人办事过吗?有时候你会遇到过官员踢球推责,你的问题在我这里能解决就解决,不能解决就推卸给另外个一个部门(对象)。至于到底谁来解决这个问题呢?政府部门就是为了可以避免屁民的请求与官员之间耦合在一起,让多个(部门)对象都有可能接收请求,将这些(部门)对象连接成一条链,并且沿着这条链传递请求,直到有(部门)对象处理它为止。
2)高峰期挤上公交车必须一个个往前递钱让前面的人把钱交给售票员,除非你运气够好,站在你前面的第一个人就是售票员,否则你的钱通常要在N个人手上传递,才能最后到达售票员手里进行处理。
在以上两个例子中,我们可以看到职责链模式的优点:
- 请求发送者只需要知道链中的第一个节点,从而弱化了发送者和一组接收者之间的强联系。如果不使用职责链模式,那么在公交车上,必须先搞清楚谁是售票员,才能把钱递给他。
- 降低耦合度 :该模式使得一个对象无需知道是其他哪一个对象处理其请求。对象仅需知道该请求会被“正确”地处理。接收者和发送者都没有对方的明确的信息,且链中的对象不需知道链的结构。
- 职责链可简化对象的相互连接 : 结果是,职责链可简化对象的相互连接。它们仅需保持一个指向其后继者的引用,而不需保持它所有的候选接受者的引用。
- 增强了给对象指派职责的灵活性 :当在对象中分派职责时,职责链给你更多的灵活性。你可以通过在运行时刻对该链进行动态的增加或修改来增加或改变处理一个请求的那些职责。你可以将这种机制与静态的特例化处理对象的继承机制结合起来使用。
- 增加新的请求处理类很方便。
订单页面时php模板,表单几个字段如下:
- orderType: 表示订单类型(定金或者普通用户),code的值为1时候是500元定金用户,为2是200元定金用户,为3则是普通用户。
- pay : 表示用户是否支付定金,用户虽然下过500元定金的订单但是如果他一直没有支付定金,那么只能降级为普通用户。
- stock : 仅用户普通用户的库存数量,定金用户不受限制。
var order = function( orderType, pay, stock){
if ( orderType === 1){ //500元定金购买模式
if ( pay === true){
console.log('500 rmb deposit, get 100 coupon');
}else{
if ( stock >0){
console.log('normal buy no coupon');
}else{
console.log('the stock lack');
}
}
}
else if ( orderType === 2){ //200元定金购买模式
if ( pay === true){
console.log('200 rmb deposit , get 50 coupon');
}else{
if ( stock >0){
console.log('normal buy no coupon');
}else{
console.log('the stock lack');
}
}
}
else if ( orderType === 3){ //200元定金购买模式
if ( stock >0){
console.log('normal buy no coupon');
}else{
console.log('the stock lack');
}
}
}
order(1,true,500); //500 rmb deposit, get 100 coupon
order(2,true,500); //200 rmb deposit , get 50 coupon
order(3,true,500); //normal buy no coupon
order(1,false,500); //normal buy no coupon
order(2,false,500); //normal buy no coupon
order(3,false,500); //normal buy no coupon
3、用职责链模式重构代码
var order500 = function ( orderType, pay, stock ) {
if( orderType === 1 && pay === true ){
console.log('500 rmb deposit, get 100 coupon ')
} else {
order200(orderType,pay,stock) // req pass to 200 order
}
};
var order200 = function ( orderType, pay, stock ) {
if( orderType === 2 && pay === true ){
console.log('200 rmb deposit , get 50 coupon')
} else{
orderNormal(orderType,pay,stock)
}
};
var orderNormal = function ( orderType, pay, stock ) {
if( stock > 0 ){
console.log('normal buy no coupon')
} else{
console.log('the stock lack')
}
};
//test result:
order500(1,true,500); //500 rmb deposit, get 100 coupon
order500(2,true,500); //200 rmb deposit , get 50 coupon
order500(3,true,500); //normal buy no coupon
order500(1,false,500); //normal buy no coupon
现在我们可以很清晰的看到,执行结果跟之前那个有很多if的代码段是一样的,这里我们已将一个大函数变成了3个小函数,去掉了许多嵌套的条件分支语句。但是这里每个函数中的链条传递顺序非常重要,而且非常僵硬,传递请求的代码被耦合在业务函数之中,这依然是违反开放-封闭原则。比如说如果有一天,我们将其中的200元订单活动改为300元订单,或者去掉200元订单,意味着我们需要去修改函数的内部代码,那这样的链条操作就会比较麻烦,所以接下来可以进行灵活拆分这些节点。
var order500 = function (orderType, pay, stock) {
if(orderType === 1 && pay === true){
console.log('500 rmb deposit, get 100 coupon ')
} else {
return 'nextSuccessor' // unknow the next node but always pass to next.
}
};
var order200 = function (orderType, pay, stock) {
if(orderType === 2 && pay === true){
console.log('200 rmb deposit , get 50 coupon')
} else{
return 'nextSuccessor';
}
};
var orderNormal = function (orderType, pay, stock) {
if(stock > 0){
console.log('normal buy no coupon')
} else{
console.log('the stock lack')
}
};
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)
//最后把请求传递给第一个节点
//test
chainOrder500.passRequest(1,true,500); //the stock lack
chainOrder500.passRequest(2,true,500); //the stock lack
通过改进,我们可以自由灵活的增加移除和链中的节点顺序,假如我们又想支持300元定金购买,那我们就在改链中增加一个节点即可,代码如下:
var order300 = function () {
// todo
};
var chainOrder300 = new Chain(order300);
chainOrder500.setNextSuccessor(chainOrder300);
chainOrder300.setNextSuccessor(chainOrder200);
这样,一个完整的职责链模式代码就完成了。