0%

javascript-clean-code-if

写好代码不是一件容易的事,代码能够按照预期效果工作只是最低要求,在这个基础上将代码写得简洁,可维护性高,可以说是一门艺术,今天读了一本书:《The art of readable code》,感触颇深,现摘取一例,与大家分享,为了便于阅读,我将代码简化并用JS重写了一下,先看原始代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
function reply(userResult, permissionResult) {
if (userResult === "SUCCESS") {
if (permissionResult !== "SUCCESS") {
console.log("error reading permissions");
console.log("done");
return;
}
console.log("success");
} else {
console.log(userResult);
}
console.log("done");
}

很简单的一段代码,就是根据输入参数的不同取值,打印不同的log,但是代码里一个嵌套的if语句,导致可读性下降,即使经验再丰富的程序员,也要停留思考一下。罗马不是一天建成的,可读性差的代码也不是一天写成的,通常随着功能的增加,维护人员的增多,代码的可读性会越来越差,其实这段代码一开始逻辑很简单。就是根据userResult的值不同,做不同的处理。

1
2
3
4
5
6
7
8
function reply(userResult) {
if (userResult === "SUCCESS") {
console.log("success");
} else {
console.log(userResult);
}
console.log("done");
}

然后突然有一天增加了一个需求,需要根据permissionResult的值来做进一步的处理,于是该函数多了一个参数,处理逻辑也跟着复杂起来,这便有了文章开头那段代码.

那么如何优化这段代码呢?可以看到外层的if有点头重脚轻的感觉,我们可以采用优先处理negative case的方式,这样可以减少嵌套,提高代码的可读性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function reply(userResult, permissionResult) {
if (userResult !== "SUCCESS") {
console.log(userResult);
console.log("done");
return;
}

if (permissionResult !== "SUCCESS") {
console.log("error reading permissions");
console.log("done");
return;
}

console.log("success");
console.log("done");
}

至此,我们就移出了嵌套的if,使这两个if变为平级结构,可读性好多了。需要注意的是,因为我们在两个if中都使用了return(return early principle),所以需要将末尾的console.log("done")上提到每个if中,否则会导致逻辑丢失。

原书写到这里就结束了,但是我想再进一步,可以改进后的代码里仍然有多个重复的console.log("done"),我们可以将其提取出来,别忘了那句名言:Don't repeat yourself

在改动之前,先讲一个原理。

if{..., return}结构转换为if-else结构

如果一个函数中有多个if,每个if中都有return,可以考虑将其转换为if-else结构。

1
2
3
4
5
6
7
8
9
10
11
12
13
function foo() {
if (condition1) { // 1
// code block
return;
}

if (condition2) { // 2
// code block
return;
}

// code block
}

等同于:

1
2
3
4
5
6
7
8
9
function foo() {
if (condition1) { // 1
// code block
} else if (condition2) { // 2
// code block
} else {
// code block
}
}

所以之前的代码可以继续优化为下面的形式

1
2
3
4
5
6
7
8
9
10
11
12
function reply(userResult, permissionResult) {
if (userResult !== "SUCCESS") {
console.log(userResult);
console.log("done");
} else if (permissionResult !== "SUCCESS") {
console.log("error reading permissions");
console.log("done");
} else {
console.log("success");
console.log("done");
}
}

最后把重复的console.log("done")提取出来即可。

1
2
3
4
5
6
7
8
9
10
function reply(userResult, permissionResult) {
if (userResult !== "SUCCESS") {
console.log(userResult);
} else if (permissionResult !== "SUCCESS") {
console.log("error reading permissions");
} else {
console.log("success");
}
console.log("done");
}

最后需要注意的是,这种逻辑性的改动,必须经过严密的单元测试才可以。因为只有两个参数,两两组合,所以测试用例有4个:

1
2
3
4
userResult = "SUCCESS", permissionResult = "SUCCESS";
userResult = "SUCCESS", permissionResult = "ERROR";
userResult = "ERROR", permissionResult = "SUCCESS";
userResult = "ERROR", permissionResult = "ERROR";