最近无锡出现了疫情…好多事都被打乱了,半夜11点多出来做核酸本来非常不开心,但是看到大白们后突然就释怀了,不得不说他们更辛苦,也理应更气愤,一弄就是整个通宵,致敬每一位因疫情而身不由己的天使们…加油…
前言
在上一章节中我们讲述了如何给函数与方法进行规范化的处理(上一章节地址:《代码规范》如何写出干净的代码(二)函数与方法),那么在这一章节,则是要主要对代码深层嵌套的问题做一些分享和处理;
耐心看完,你一定有所收获;
原因
个人感觉,生成代码深层嵌套的问题在实际业务开发中大概率都是因为逻辑判断,而这些逻辑判断以及判断后续的代码也没有进行拆分,在没有拆分的基础上 基本就会写成深层嵌套,因此在代码整体上就会显得非常臃肿,话不多说,先看个来自网络的例子吧
function processTransactions(transactions) {
if (transactions && transactions.length > 0) {
for (const transaction of transactions) {
if (transaction.type === "PAYMENT") {
if (transaction.status === "OPEN") {
if (transaction.method === "CREDIT_CARD") {
processCreditCardPayment(transaction);
} else if (transaction.method === "PAYPAL") {
processPayPalPayment(transaction);
} else if (transaction.method === "ALIPAY") {
processAlipayPayment(transaction);
}
} else {
console.log("交易类型无效!");
}
} else if (transaction.type === "REFUND") {
if (transaction.status === "OPEN") {
if (transaction.method === "CREDIT_CARD") {
processCreditCardRefund(transaction);
} else if (transaction.method === "PAYPAL") {
processPayPalRefund(transaction);
} else if (transaction.method === "ALIPAY") {
processAlipayRefund(transaction);
}
} else {
console.log("交易类型无效!", transaction);
}
} else {
console.log("交易类型无效!", transaction);
}
}
} else {
console.log("不提供交易!");
}
}
是不是看的很头疼,代码相对比较难以阅读和理解,当然也就不能称之为干净的代码;实际我们的业务开发中肯定会遇到非常多的类似场景,做各种条件判断,一旦条件判断过多,那么 代码的层级就会嵌套的非常深 ,以至于当别人进行阅读的时候,那么就会出现上面这个例子的结果,非常难以阅读,那么我们该如何优化呢,个人觉得大致有以下几种:
解决方案
代码守卫
首先什么是守卫,简单的说就是 反向检查条件,以达到减少代码层数嵌套的目的,先看个简单的例子:
if(email.includes("@")){
// ...业务代码
}
将其改写成如下方式
if(!email.includes("@")){
return
}
// ...业务代码
**这样写有什么好处?**细想一下逻辑,上面一种情况我们需要判断email字符串中是否包含"@"符号,一旦包含,那么执行业务代码,那么到这里 就已经形成了一层嵌套;
那下面一种呢?下面一种它使用了** 反向验证**,一旦验证通过,验证出此时不是一个邮箱,那么就会走retrun,而是邮箱时,会走下面的业务代码,这种方式和上面那种比有一个非常明显的优势,没有形成一层嵌套,再看一个例子吧,两层嵌套
if(isLogin){
if(user.code === 10001){
// ...业务代码
}
}
// 使用代码守卫
if(!isLogin) {
return;
}
if(user.code !== 10001){
return;
}
// ...业务代码
这样应该有点明白了吧,下面就使用这种方式修改上面的例子代码,注意,这里仅使用代码守卫的方式改写例子,不使用之前说的一些拆分之类的方法
function processTransactions(transactions) {
if (!transactions || transactions.length <= 0) {
console.log("不提供交易!");
return;
}
for (const transaction of transactions) {
if (transaction.type !== "PAYMENT" || transaction.type !== "REFUND") {
console.log("交易类型无效!", transaction);
continue;
}
if (transaction.type === "PAYMENT") {
if (transaction.status !== "OPEN") {
console.log("交易类型无效!");
continue;
}
if (transaction.method === "CREDIT_CARD") {
processCreditCardPayment(transaction);
} else if (transaction.method === "PAYPAL") {
processPayPalPayment(transaction);
} else if (transaction.method === "ALIPAY") {
processAlipayPayment(transaction);
}
}
if (transaction.type === "REFUND") {
if (transaction.status !== "OPEN") {
console.log("交易类型无效!");
continue;
}
if (transaction.method === "CREDIT_CARD") {
processCreditCardRefund(transaction);
} else if (transaction.method === "PAYPAL") {
processPayPalRefund(transaction);
} else if (transaction.method === "ALIPAY") {
processAlipayRefund(transaction);
}
}
}
}
改写完了之后,层数确实得到了一些减少,再回过来比对两段代码,不知道有没有小伙伴发现,其实我们的代码守卫是在消除哪一层嵌套?是else这一层,也就是说所有else这个分支其实都可以使用代码守卫来改写,一旦改写那么层数就会相应的减少,也就能使得我们的代码相对更加干净,更加整洁;
错误处理
错误处理指的是什么呢?我个人的理解就是:使用抛出异常错误来代替if语句,这样可以减少逻辑判断增加代码的函数功能,再换个说法,如果某些逻辑判断是用于判断错误的,那么就让它成为一个错误,而不是其他,看个例子吧
if(!email.includes("@")){
return {
code: 10001,
message: "邮箱验证失败"
}
}
这种其实不是错误,或者说它其实没有形成错误,错误其实在Jacascript中是有专门的构造函数的,就是Error()函数,如下
if(!email.includes("@")){
const error = new Error("邮箱验证失败");
error.code = 10001;
throw error;
}
一旦抛出了错误,那么就可以通过try…catch进行捕获,从而减少if逻辑的数量,看个例子吧
function isEmail(email) {
const res = email.includes("@");
return {
code: res ? 10000 : 10001,
message: res ? "验证通过" : "邮箱验证失败",
};
}
const result = isEmail(email);
if(result.code === 10001){
console.log(result.message)
return;
}
// 业务代码
那么如果用抛出错误的方式改写后会怎么样
function isEmail(email) {
if(!email.includes("@")){
const error = new Error("邮箱验证失败");
error.code = 10001;
throw error;
}
}
try {
isEmail(email);
} catch (error) {
console.log(error.message);
}
这样,if的判断就会被去掉了,因此也就是说适当的错误处理可以减少if的条件判断语句;
错误处理原则
错误处理的原则其实就是一句话,每一个错误的处理都必须专注于一件事,怎么理解呢,简单的说就是一个错误处理只对应一个错误,最好不要一个try…catch…中有很多个异常或者完全不同的异常抛出,比如:
function processTransactions(transactions) {
for (const transaction of transactions) {
try {
validateEmail(transaction)
// 业务代码...
} catch (error) {
console.log(error.message);
}
}
}
这种try…catch…其实就有点不是那么合适,为什么?因为每一次for循环都会被触发一次try…catch…完全失去了错误处理本来的意义,并且结构上也没有那么清晰了,因此我们就需要稍微改一改
function processTransactions(transactions) {
for (const transaction of transactions) {
validateEmail(transaction)
}
}
function validateEmail(transaction){
try {
// 业务代码...
} catch (error) {
console.log(error.message);
}
}
当然,还是再次强调我们最初的目的:干净代码的本质是为了更友好的使人阅读代码,因此代码的结构与层次等等都是因团队而异,这里我只是在描述我学到的一些觉得好的点,并不代表这就是对的…
小结
在本文中,简单讲述了一些代码结构及代码错误时的处理,大致如下:
- 可以使用反向验证来代替正向验证达到减少代码层嵌套的问题;
- 适当的异常处理也可以减少代码嵌套的问题;