这是我参与更文挑战的第4天,活动详情查看: 更文挑战
一、什么是职责链模式
职责链模式就是当一个对象 a
,有多种可能的请求对象 b
、c
、d
、e
时,我们为 b
、c
、d
、e
分别定义一个职责,组成一条职责链,这样 a
只需要找到 b
发起请求,然后沿着职责链继续请求,直到找到一个对象来处理 a
。
女孩子们都喜欢结伴吃饭,我现在要找一个人一起吃饭,代码如下:
嗯…..女程序员确实是这样,吃个饭也要写代码发请求的。
const [ astrid, brooke, calliope ] = [{
name: 'astrid',
requirement: '我要吃湘菜'
},{
name: 'brooke',
requirement: '我要找10个人一起吃饭'
},{
name: 'calliope',
requirement: '我要和男朋友一起吃饭'
}]
// 是否满足Astrid的要求
function isSatisfyAstrid (user) {
// ... 省略...
}
// 是否满足Brooke的要求
function isSatisfyBrooke (user) {
// ... 省略...
}
// 是否满足Calliope的要求
function isSatisfyCalliope (user) {
// ... 省略...
}
function eatDinner () {
if (isSatisfyAstrid()) {
console.log(`我可以和 astrid 一起吃晚饭啦`)
} else if (isSatisfyBrooke()) {
console.log(`我可以和 brooke 一起吃晚饭啦`)
} else if (isSatisfyCalliope()) {
console.log(`我可以和 calliope 一起吃晚饭啦`)
} else {
console.log(`哎呀,我要一个人吃晚饭啦`)
}
}
复制代码
由于 astrid
、brooke
、calliope
吃晚饭的要求不同,我需要一个个去发起晚餐请求,直到找到答应和我一起吃晚饭的人。
在这里,我假设 astrid
的要求是要吃湘菜,brooke
的要求是要找 10 个人凑一桌一起吃,calliope
的要求是只想和男朋友一起吃饭。
上述代码用 if-else
的做法非常死板,假如我又多了个朋友 davi
,我必须再次修改 eatDinner
方法,违反了开放-封闭原则,不易于维护。
下面使用职责链来优化上述代码,代码如下:
// 给每个人定义一个职责
const chainOrderA = new Chain(isSatisfyAstrid)
const chainOrderB = new Chain(isSatisfyBrooke)
const chainOrderC = new Chain(isSatisfyCalliope)
// 设置一下职责链的顺序
chainOrderA.setNextSuccessor(chainOrderB)
chainOrderB.setNextSuccessor(chainOrderC)
// 发起请求,这时我只需要向职责链上的第一个人请求
function eatDinner () {
chainOrder.passRequest() // 发起请求
}
复制代码
将职责作为转入 Chain
函数,并通过 setNextSuccessor
定义该职责的下一个职责函数,组成一条 chainOrderA
-> chainOrderB
-> chainOrderC
职责链,这时,我只需要向 astrid
发起请求,如果请求失败,将会沿着职责链继续请求,直到找到和我一起吃晚饭的人。
下面将讲述在实际场景中怎么使用职责链模式,怎么实现 Chain
方法,请继续往下看。
二、实际场景
1. 618 预售商品订单
下周就是 618,电商网站免不得会推出商品预售活动,假设在 618 之前,预付 500 定金,可获得 100 元优惠券,预付 200 元定金,可获得 50 优惠券,未付定金则无优惠券。618 当天的购买事件如下:
1.1 普通做法
先上代码。
本文代码仅举例说明,和业务无关。
const order = function (orderType) {
if (orderType === 500) {
console.log('已预付500定金,享有100优惠券')
} else if (orderType === 200) {
console.log('已预付200定金,享有50元优惠券')
} else {
console.log('未付定金,无优惠')
}
}
order(500) // '已预付500定金,享有100优惠券'
复制代码
熟悉的代码,一长段的 if-else
判断,不利于维护。
1.2 职责链模式
定义一个职责类 Chain
。
- 接收一个职责函数
fn
作为参数; setNextSuccessor
指定该职责的下一个职责函数;passRequest
发起对职责函数fn
的请求;- 如果返回结果是
nextSuccesstor
,说明请求失败,继续请求职责链上的下一个职责函数; - 如果不是返回
nextSuccesstor
,说明找到了接收请求的对象,返回请求结果,不再继续执行职责链上的下一个职责函数。
- 如果返回结果是
代码如下:
const Chain = function(fn) {
this.fn = fn;
this.successor = null;
};
Chain.prototype.setNextSuccessor = function(successor) {
return this.successor = successor;
}
Chain.prototype.passRequest = function() {
const ret = this.fn.apply(this, arguments)
if (ret === 'nextSuccessor') {
return this.successor && this.successor.passRequest.apply(this.successor, arguments);
}
return ret;
};
复制代码
然后定义职责类实例,通过 setNextSuccessor
组成职责链,代码如下:
const order500 = function (orderType) {
if (orderType === 500) {
console.log('已预付500定金,享有100优惠券')
} else {
return 'nextSuccessor'
}
}
const order200 = function (orderType) {
if (orderType === 200) {
console.log('已预付200定金,享有50元优惠券')
} else {
return 'nextSuccessor'
}
}
const chainOrder500 = new Chain(order500)
const chainOrder200 = new Chain(order200)
chainOrder500.setNextSuccessor(chainOrder200)
chainOrder500.passRequest(200)
复制代码
上述代码将 chainOrder500
和 chainOrder200
组成一条职责链,不管用户是哪种类型,都只需要向 chainOrder500
发起请求,如果 chainOrder500
无法处理请求,就会继续沿着职责链发起请求,直到找到能处理请求的职责方法。
通过职责链模式,解耦了请求发送者和多个接收者之间的复杂关系,不再需要知道具体哪个接收者来接收发送的请求,只需要向职责链的第一个阶段发起请求。
三、小结
职责链模式,帮助我们管理代码,降低发起请求和接收请求对象之间的耦合。
职责链模式中的节点数量和顺序是可以自由变动的,可以在运行时决定链中包含哪些节点。
可通过 github源码 进行实操练习。
希望能对你有所帮助,感谢阅读~别忘了点个赞鼓励一下我哦,笔芯❤️
· 往期精彩 ·