代码重构相关内容
分解复杂条件表达式
复杂的表达式
程序中,最常导致代码复杂度上升的地方就是复杂的条件逻辑。很多时候我们需要根据各种状态来执行不同的处理操作。随着业务复杂度的提升,我们会得到一个非常多类型参与的判断条件。复杂的条件判断导致代码难以被阅读。而且随着参与判断的参数增多,错误的判断导致的BUG也会产生。并且很多时候我们根据判断条件排查其进入的业务分支时,很难根据判断条件理解其处理场景。
更严重的是,如果表达式中存在多种不同的连接符时,如何正确理解表达式的执行顺序都会成为一种困难的事情。
和方法分解类似,如果有大的表达式,我们可以将其根据业务场景、数据模型等区分方式进行归类整理,类似的判断独立成一个方法,为其提供一个可以被理解的名称。将多个方法整合在一起,来替换原始的条件表达式。这样仅仅根据方法名称我们可以直接理解每个判断分支的作用。
如何分解表达式
- 首先要理解这个表达式,确保对表达式的含义和计算结果有足够的理解。
- 将表达式单独拿出来,进行拆借。这里我经常使用两种方式。一种方式是将容易被理解的内容拆解出来,然后留下难以理解的部分单独处理。另外一种方式将表达式分解成两个相等长度的子表达式,然后依次拆解知道拆解出来的子表达式都是可以被轻松理解的。
- 为每个子表达式添加有意义的变量名,以便更好地描述子表达式的含义和作用。
- 使用新的变量组合新的表达式。
- 测试。
下面就是一个分解表达式的例子
public class Main {
public static void main(String[] args) {
int x = 5;
int y = 3;
int z = 8;
boolean result = isExpressionValid(x, y, z);
System.out.println(result);
}
public static boolean isExpressionValid(int x, int y, int z) {
// 复杂的条件表达式
boolean expression = (x > y || x < z) && (x + y > z && x - y < z);
// 分解为多个简单的条件表达式
boolean condition1 = x > y || x < z;
boolean condition2 = x + y > z;
boolean condition3 = x - y < z;
// 组合简单的条件表达式
boolean result = condition1 && condition2 && condition3;
return result;
}
}
将一个复杂的表达式,拆分成三个简单的条件表达式,并将其组合为一个新的布尔值。这样可以使代码更易于理解,减少错误发生的可能性
合并简单条件表达式
合并条件表达式分两种情况:第一种是表达式非常简单,使用将其合并在一起;另外一种是表达式进入的逻辑分支相同,那么合并这些分支相同的表达。两种方式都可以让代码变的简洁。
简单的表达式
很多时候即使将多个简单的表达式合并为一个复杂的表达式,最终的表达式依旧是个容易识别的表达式时,将他们合并起来是个好的方法。比如下面这个逻辑
boolean condition1 = a > b;
boolean condition2 = c > d;
if (condition1 && condition2) {
System.out.println("a is greater than b and c is greater than d");
}
即使将逻辑修改为下面结构,也并不会让人觉得很难理解
// 合并表达式后的代码
if (a > b && c > d) {
System.out.println("a is greater than b and c is greater than d");
}
相同的逻辑分支
很多时候表达式判断的条件不同,但是最终进入的逻辑分支却是同一个。这个时候可以尝试将其合并为同一个判断条件。这个时候通过合并表达式我们可以更清楚了解哪些不同的条件进去的却是同一个逻辑。
重构前
public boolean isAdult(Person person) {
if (person.getAge() >= 18) {
if (person.getGender().equals("male")) {
return true;
} else {
return false;
}
} else {
return false;
}
}
重构后
public boolean isAdult(Person person) {
return person.getAge() >= 18 && person.getGender().equals("male");
}
但在合并逻辑分支的时候需要考虑一个问题。很多时候如果不同的判断却进入了相同的分支,这个时候需要考虑这些逻辑是否是因为相同的行为或者特性进入这些分支。在合并这些判断逻辑的时候,考虑是否能将这些判断提炼出单独的条件方法。并为方法提供一个有意义的名称。他们是符合了哪些行为而进入这条分支。
为什么不合并逻辑分支
有一种特殊情况下,我们可以不去合并逻辑分支。就是这个判断是故意独立出来的内容。它是用来提醒代码阅读者,这部分判断是独立且重要的。有可能是一个重要标记也有可能是个特殊处理。总之这种情况可以不需要进行合并。
以卫语句取代嵌套条件表达式
逻辑泥潭
很多时候业务中的判断是分层的,我们需要判断上层内容然后再去进行下一层代码的判断,随着参数众多层级众多,我们的判断代码可能成为这个样子
if(true){
if(true){
if(true){
if(true){
if(true){
}
}
}
}
}
这样的代码对于任何阅读者都是一个灾难。一层一层嵌套的判断,加上复杂的逻辑,在看这些代码的时候只要分神,我们可能都无法确认当前逻辑存在的层级。
卫语句
上面那种情况我们可以使用卫语句来解决。这里先介绍下卫语句(Guard Clause),卫语句指的是在程序中用于检查一些特定条件是否成立的语句,如果条件不成立,立即终止函数的执行并返回。以上面的内容为例子,如果使用卫语句实现就是下面内容
if (false) {
return;
}
if (false) {
return;
}
if (false) {
return;
}
if (false) {
return;
}
if (false) {
return;
}
什么时候使用卫语句
首先要明确的是卫语句只适用于一些特定的情况。很多时候我们都要求函数只允许有一个入口和出口。但是在下面情况下可以考虑使用卫语句。
- 当嵌套条件表达式的层数过多时。过多的层级会让阅读者迷失在不同层级内容。这个时候可以考虑使用卫语句减少层级。
- 当嵌套条件表达式存在边界条件时,这个时候可以使用卫语句直接跳出。
如何使用卫语句重构
如果考虑将if语句改造为卫语句需要按照下面步骤
- 首先确定你的if代码需要改造。如果其逻辑并不是一个复杂的或者多层级的判断,改造成卫语句可能并不能带来更好的提升。
- 找到那些可以根据取反条件直接跳出循环的条件语句,将其将条件判断取反,改写为卫语句。
- 然后进行测试。
- 如果卫语句改造后,剩下的条件判断可能产生相同的结果,此时可以尝试进行表达式合并的动作。