重构复杂if表达式的最佳方式
Best way to refactor complex if expression
我刚刚加入了一个大型电子商务项目,该项目使用了angularJS前端。我的任务是在结帐页面上添加许多复杂的功能。其中已经有很多复杂的逻辑。
为了让事情变得更难,我不断遇到许多if
语句表达式,就像下面的一个,这使得它很难理解,这是一个缓慢的过程,通过许多这些类型的if
表达式。
其中一些表达非常关键,有时甚至更长…没有单元测试,当我问其他开发人员这是在检查什么和为什么(只是为了确保我理解),我通常被指向别人,而不是一个解释。
if ((!previousBillingAddress) || (previousBillingAddress && previousBillingAddress.id !== bag.paymentAddress.id)){
console.log('all of the above was true'); // just a dummy log
} else {
console.log('all of the above was false'); // just a dummy log
}
有没有人有好的技巧来重构这些类型的表达式?
我想把它们分解成具有描述性名称的函数,函数可以返回true
或false
,但我不确定是否有更好的方法。
A = previousBillingAddress
B = previousBillingAddress.id !== bag.paymentAddress.id
则表达式为:
if (!A || (A && B)) {
log1
} else {
log2
}
我们可以用!A || (A && B)
做什么?它等于!A || B
:
A | B | !A | A && B | !A || (A && B) | !A || B
==========================================================
1 | 1 | 0 | 1 | 1 | 1
1 | 0 | 0 | 0 | 0 | 0
0 | 1 | 1 | 0 | 1 | 1
0 | 0 | 1 | 0 | 1 | 1
这就是为什么表达式等于:
if (!previousBillingAddress || previousBillingAddress.id !== bag.paymentAddress.id) {
console.log('all of the above was true'); // just a dummy log
} else {
console.log('all of the above was false'); // just a dummy log
}
TL;博士
上表仅检查!A || (A && B)
是否等于!A || B
。如何猜出!A || B
?对于这样的表达式,最好遵循以下规则:
A == !(!(A)) (rule 1)
!(A && B) == !A || !B (rule 2)
!(A || B) == !A && !B (rule 3)
A && (B || C) == A && B || A && C (rule 4)
我们有!A || (A && B)
,开始玩。根据规则1,它等于
!(!(!A || (A && B)))
现在我们使用规则3:
!(!(!A || (A && B))) == !(A && !( A && B))
规则2:!(A && !( A && B)) == !(A && (!A || !B)) (*)
由于规则4:
A && (!A || !B) == (A && !A) || (A && !B)
我们有(A && !A) || (A && !B)
,它可以减少到(A && !B)
。现在我们可以回到(*)
,我们有:
!(A && (!A || !B)) == !((A && !A) || (A && !B)) == !(A && !B)
根据规则2,我们得到:
!(A && !B) == !A || B
您可以删除previousBillingAddress &&
部分-在||
的第二个操作数中,您已经确定previousBillingAddress
不是假的。这将使总体条件
if (!previousBillingAddress || previousBillingAddress.id !== bag.paymentAddress.id) {
console.log('all of the above was true'); // just a dummy log
} else {
console.log('all of the above was false'); // just a dummy log
}
对我来说足够短了。如果没有,做一个适当命名的辅助函数,你传递previousBillingAddress
和bag
.
在我个人看来,我认为你可以重构和封装函数内部的验证/检查,然后如果相同的验证适用于你的代码(文件/模块等)的其他部分,你可以重用它。在这种情况下,你使用的是angular,所以最好使用angular自定义服务来进行这种类型的业务验证,因为它们也可以操纵作用域
注释是我们最好的朋友。您希望确保下一个查看您代码的人知道确切地知道它应该做什么,并且从那里可以更快更容易地看到确切地看到它在做什么。
在这种情况下,if语句中的表达式可以缩短。下面是我用伪代码的方式注释它的方式:
// we need to check if the user has, and is using, a previous billing address
if (previousBillingAddress && previousBillingAddress.id === bag.paymentAddress.id)){
// the user is using a previous billing address
// do some work with the previous billing address
} else {
// the user is using a new address
// do some work with the new address
// save the new billing address
}
注意previousBillingAddress
是这个if语句的主体。如果我们正在检查previousBillingAddress,那么在我们的第一个块中,我们要确保我们处理了如果我们有一个以前的账单地址会发生什么。这样更合乎逻辑。
如果我们没有以前的账单地址,那么检查我们是否有以前的账单地址就没有意义了,如果我们有,我们该怎么办。
看这是多么干净和合乎逻辑!& lt; 3
我建议将复杂的条件提升为布尔变量(或者常量,如果你支持的JS级别允许你使用const
)。例如:
var weakPrevBillingAddress =
!previousBillingAddress ||
(previousBillingAddress &&
(previousBillingAddress.id !== bag.paymentAddress.id));
if (weakPrevBillingAddress) {
console.log('judged weak previousBillingAddress');
// other processing expected here
} else {
console.log('judged strong previousBillingAddress');
// other processing expected here
}
正如您和philip yoo所建议的,您可以将条件提升为布尔帮助函数。但是,大多数定义此类函数的方法将使逻辑远离使用点,从而使理解变得更加困难。布尔变量(布尔变量)"谓词")可以很容易地定义接近他们的使用(例如,在你的if
语句之前)。所以没有太多额外的距离,但是它们仍然把布尔值的计算和它的使用分开。单独关注计算的能力可以使事情变得更简单、更容易理解。如上所示,它还允许您分割长线条和缩进使用更好的清晰度。
我不知道你是否有重命名现有变量名的灵活性。如果你这样做,你正在使用的是非常长的。长名字有助于增进理解,但每行重复几次的22个字符的名字会分散注意力,而不是清晰。较短的名称(用于源值或仅用于您计算的谓词)会有所帮助。例如:
var weakPrevBA = !prevBA ||
(prevBA.id !== bag.paymentAddress.id);
选择好的变量名是一门艺术,开发人员对"多少描述才足够"的品味各不相同。但在一个一直处理账单地址的应用程序中,一行接一行地拼写billingAddress
并不一定有帮助。如果您正在阅读代码和注释,您可能会高度意识到您正在处理帐单地址。如果没有,请添加一个快速注释,而不是每行多次拼写完整的概念。
注意,在这个较短的示例中,我还简化了布尔表达式。(这也是Bergi提出的建议)这是个好主意——只要你确定它可以被正确地简化。但是,这样做必须非常小心。您没有单元测试,因此您没有简单的方法来测试简化是否真的相同。而且你正在处理复杂的代码。我建议保留原始的布尔表达式,至少在开始时(可能使用缩短的名称)。至少在您更全面地理解代码之前,这减少了bug可能出现的地方。你对代码理解得越好,你做得越简单(例如,通过在条件中添加计算谓词,而不是长而复杂的表达式),你在重写表达式时就越自由和安全。
像这样
var billingId = previousBillingAddress && previousBillingAddress.id;
if (billingId || billingId === bag.paymentAddress.id){
console.log('all of the above was false'); // just a dummy log
} else {
console.log('all of the above was true'); // just a dummy log
}
what I did
- 因为你正在使用
if
和else
,在if语句中使用否定不是一个好的做法。所以我去掉了
如果你只想要反的部分(没有else)
if (!(billingId || billingId === bag.paymentAddress.id)){
console.log('all of the above was true'); // just a dummy log
}
- 的,,将返回第一个false值,或最后一个true值。所以
billingId
等于false
或者id
。在此基础上,你的第一个if是检查它的"falsey"
是否继续,所以你也可以使用相同的变量。
- 在localhost Dev Box上测试JSONP请求的最佳方式
- 为react组件传递道具的最佳方式
- 让Webpack管理Quirky AMD定义的最佳方式
- 在承诺链中处理早期回报的最佳方式
- 在ng重复循环中显示条件内容的最佳方式是什么
- 在phonegap中为android调用onload函数的最佳方式
- 链接两个网页或网络应用程序的最佳方式
- 什么's是连接供应商js文件的最佳方式
- 什么's是在javascript中迭代项的最佳方式
- 在node.js中编写单元测试的最佳方式是什么
- 在页面上记录数据并实现pushstate()的最佳方式
- 使用AJAX在Rails中提交动态表单的最佳方式是什么
- 什么'这是加载jQuery的最佳方式
- 将大数组(字符串和类型数组的混合物)存储到blob或文件中/从blob或文件检索大数组的最佳方式
- 在网站上显示.mov作为加载屏幕的最佳方式
- 使用Angular存储用户以前是否选中过复选框,然后再调用它的最佳方式是什么
- 以角度渲染表中数据的最佳方式
- 删除集合中旧邮件/帖子的最佳方式
- 显示全屏Ajax加载程序的最佳方式
- PHP和JS中表单验证的最佳方式