起因
前几天在优化Nodejs服务项目的一个Controller代码,忽然感觉里面的代码似乎有点别扭过头了——if-else
太多了,下面是它代码的一个大概样子:
async userTagsController(ctx) {
let findText = ctx.params.userinfo;
let result = model.getUserInfo(findText); // 从数据库取数据,没找到则返回空对象
if (isObjectNull(result)) {
if (result.is_blocked == 0) {
if (result.tags !== "") {
let outData = result.tags.split("#");
ctx.succeed(outData);
}
else {
// 未设置标签时的错误返回
}
}
else {
// 用户被封禁时的错误返回
}
}
else {
// 未找到用户时的错误返回
}
}
改造大概
于是,咱决定将一些逻辑抽取出来,然后加点细节,最后就变成了这个样子:
async userTagsController(ctx) {
let findText = ctx.params.userinfo;
let result = model.getUserInfo(findText);
let chain_isObjectNull = new Chain(unit_isObjectNull);
let chain_isBlocked = new Chain(unit_isBlocked);
let chain_outputTags = new Chain(unit_outputTags);
chain_isObjectNull.after(chain_isBlocked);
chain_isBlocked.after(chain_outputTags);
let outData = chain_isObjectNull.begin(result);
ctx.succeed(outData);
}
这一种看起来是不是简洁很多,而且它所做的事也是一样的。
那个细节,是一种叫 “责任链” 的设计模式,(个人理解)它可以将一个请求处理流程分开成一个个方法(比如条件判断),再将那些方法组合起来形成链条。需要的请求就沿着这条链条传下去,直到某个方法有条件去处理这个请求。
这样处理起来,感觉还很灵活。
摘自菜鸟教程的介绍: 顾名思义,责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式。
在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。
关键代码
先建立一个文件,叫chain.js
,里面用来存放责任链实现的关键代码:
class Chain {
constructor (fn) {
this.fn = fn;
this.successor = null;
}
after (successor) {
return this.successor = successor;
}
begin () {
let ret = this.fn.apply (this, arguments);
if (ret === "after") {
return this.successor && this.successor.begin.apply (this.successor, arguments);
}
return ret;
}
}
module.exports = Chain;
拆分组合
回到刚刚开头的那一段代码,拆开处理流程吧:
function unit_nullObj (data) {
if (!isNullObj (data)) {
// 未找到用户时的错误返回
}
else {
return "after";
}
}
function unit_isBlocked (data) {
if (data.is_blocked === 1) {
// 用户被封禁时的错误返回
}
else {
return "after";
}
}
function unit_outputTags (data) {
if (result.tags !== "") {
let outData = result.tags.split("#");
ctx.succeed(outData);
}
else {
// 未设置标签时的错误返回
}
}
然后require刚刚写的chain.js
就可以开始组合了,组合完成后大概就长这个样子:
const Chain = require ("@commons/chain");
// function unit_nullObj (data) ...
// function unit_isBlocked (data) ...
// function unit_outputTags (data) ...
async userTagsController(ctx) {
let findText = ctx.params.userinfo;
let result = model.getUserInfo(findText);
let chain_isObjectNull = new Chain(unit_isObjectNull);
let chain_isBlocked = new Chain(unit_isBlocked);
let chain_outputTags = new Chain(unit_outputTags);
chain_isObjectNull.after(chain_isBlocked); // 将封禁判断置于判断空对象之后
chain_isBlocked.after(chain_outputTags); // 同理
let outData = chain_isObjectNull.begin(result); // 链条从此开始
ctx.succeed(outData);
}
测试一下,跟之前的输出基本无异。
突然有一天,这个controller
需要判断敏感词,就不用再往一堆堆的if里小心翼翼地加了,直接这样就行:
async userTagsController(ctx) {
// ...
let chain_hasSensibility = new Chain(unit_hasSensibility);
// ...
chain_isBlocked.after(chain_hasSensibility);
chain_hasSensibility.after(chain_outputTags);
let outData = chain_isObjectNull.begin(result);
ctx.succeed(outData);
}
后记
参考博客:
https://blog.csdn.net/wjh1840226173/article/details/115230287