1. 概述
在使用if
语句时,我们可能需要在其内部使用逻辑运算符(如AND或OR)处理多个条件。这可能不是一种干净的设计,会影响代码的可读性和认知复杂度。
在这篇教程中,我们将探讨如何在if
语句中格式化多个值条件的替代方法。
2. 我们能避免if
语句吗?
假设我们有一个电子商务平台,并为特定月份出生的人设置折扣。让我们看一个代码片段:
if (month == 10 || month == 11) {
// doSomething()
} else if(month == 4 || month == 5) {
// doSomething2()
} else {
// doSomething3()
}
这可能会导致难以阅读的代码。即使测试覆盖率良好,随着条件的增加,维护起来也可能变得困难,例如,我们需要将不同的条件组合在一起才能执行特定操作。
2.1. 使用整洁的代码
我们可以应用模式来替换多个if
语句。例如,我们可以将if
中多个条件的逻辑移到类或枚举中。在运行时,根据客户端输入切换接口。类似地,可以考虑使用策略模式。
这并不直接关系到格式化,通常会促使我们重新思考逻辑。不过,这是我们可以考虑的一种设计改进方法。
2.2. 提升方法语法
只要代码易于阅读和维护,使用if
/else
逻辑并没有错。例如,考虑以下代码片段:
if (month == 8 || month == 9) {
return doSomething();
} else {
return doSomethingElse();
}
首先,我们可以删除else
部分:
if (month == 8 || month == 9) {
return doSomething();
}
return doSomethingElse();
此外,其他代码也可以改进,例如,用java.time
包中的枚举替换月份数字:
if (month == OCTOBER || month == NOVEMBER || month == DECEMBER) {
return doSomething();
}
// ...
这些都是简单但有效的代码优化。因此,在应用复杂模式之前,我们应该先确保代码的可读性。
我们还将学习如何使用函数式编程。在Java中,从8版开始,可以使用lambda表达式语法实现这一点。
3. 测试说明
以电子商务折扣示例为基础,我们将创建测试并检查折扣月份内的值。例如,十月到十二月。否则我们将断言为假。我们将设置随机的月份,可能是允许的,也可能是不允许的:
Month monthIn() {
return Month.of(rand.ints(10, 13)
.findFirst()
.orElse(10));
}
Month monthNotIn() {
return Month.of(rand.ints(1, 10)
.findFirst()
.orElse(1));
}
尽管可能存在多个if
条件,为了简化,我们将假设只有一个if
/else
语句。
4. 使用switch
使用if
逻辑的一个替代方案是switch
语句。让我们看看如何在我们的例子中使用它:
boolean switchMonth(Month month) {
switch (month) {
case OCTOBER:
case NOVEMBER:
case DECEMBER:
return true;
default:
return false;
}
}
请注意,如果需要,它会向下遍历并检查所有有效月份。此外,我们可以使用Java 12的新switch
语法进行改进:
return switch (month) {
case OCTOBER, NOVEMBER, DECEMBER -> true;
default -> false;
};
最后,我们可以做一些测试来验证值是否在范围内:
assertTrue(switchMonth(monthIn()));
assertFalse(switchMonth(monthNotIn()));
5. 使用集合
我们可以使用集合来分组满足if
条件的元素,并检查某个值是否属于其中:
Set<Month> months = Set.of(OCTOBER, NOVEMBER, DECEMBER);
让我们添加一些逻辑来查看集合是否包含特定值:
boolean contains(Month month) {
if (months.contains(month)) {
return true;
}
return false;
}
同样,我们可以添加单元测试:
assertTrue(contains(monthIn()));
assertFalse(contains(monthNotIn()));
6. 使用函数式编程
我们可以使用函数式编程将if
/else
逻辑转换为函数。这样,我们的方法调用方式就具有可预测性。
6.1. 简单谓词
我们仍然使用contains()
方法,但这次将其转换为一个使用Predicate
的lambda表达式:
Predicate<Month> collectionPredicate = this::contains;
现在,我们确信Predicate
是不可变的,没有中间变量。其结果是可预测的,如果需要,可以在其他上下文中重用。
让我们用test()
方法来验证它:
assertTrue(collectionPredicate.test(monthIn()));
assertFalse(collectionPredicate.test(monthNotIn()));
6.2. 谓词链
我们可以将多个Predicate
连接起来,以实现or
条件:
Predicate<Month> orPredicate() {
Predicate<Month> predicate = x -> x == OCTOBER;
Predicate<Month> predicate1 = x -> x == NOVEMBER;
Predicate<Month> predicate2 = x -> x == DECEMBER;
return predicate.or(predicate1).or(predicate2);
}
然后将其插入if
中:
boolean predicateWithIf(Month month) {
if (orPredicate().test(month)) {
return true;
}
return false;
}
让我们通过测试确认这是否有效:
assertTrue(predicateWithIf(monthIn()));
assertFalse(predicateWithIf(monthNotIn()));
6.3. 谓词在流中
类似地,我们可以在流过滤中使用Predicate
。Lambda表达式将替代并增强if
逻辑。最终,if
将消失。这是函数式编程的优点,同时保持良好的性能和可读性。
让我们在解析输入月份列表时进行测试:
List<Month> monthList = List.of(monthIn(), monthIn(), monthNotIn());
monthList.stream()
.filter(this::contains)
.forEach(m -> assertThat(m, is(in(months))));
我们也可以使用predicateWithIf()
而不是contains().*
。如果支持方法签名,lambda没有限制,例如,它可以是静态方法。
7. 总结
在这篇教程中,我们学习了如何提高if
语句中多个条件的可读性。我们看到了如何使用switch
作为替代。此外,我们还了解了如何使用集合检查值是否包含。最后,我们了解了如何采用函数式编程方法,使用lambda表达式。Predicate
和Stream
减少了错误的可能性,提高了代码的可读性和维护性。
如往常一样,本文档中的代码可在GitHub上的教程仓库中找到。