isBooleanTooLongAndComplex

isBooleanTooLongAndComplex Thursday, April 25, 2024 週四, 25 四月 2024 This is another post in our Code Health series. A version of this post originally appeared in Google bathrooms worldwide as a Google Testing on the Toilet episode. You can download a printer-friendly version to display in your office. 这是我们 Code Health 系列的另一篇文章。这篇文章的一个版本最初出现在全球的谷歌浴室中,作为谷歌对厕所的测试一集。您可以下载适合打印的版本以在您的办公室中显示。 By Yiming Sun 作者:Yiming Sun

You may have come across some complex, hard-to-read Boolean expressions in your codebase and wished they were easier to understand. For example, let's say we want to decide whether a pizza is fantastic: 您可能在代码库中遇到过一些复杂、难以阅读的布尔表达式,并希望它们更容易理解。例如,假设我们想决定披萨是否很棒:

// Decide whether this pizza is fantastic. 决定这个披萨是否很棒。

if ((!pepperoniService.empty() || sausages.size() > 0) if ((!pepperoniService.empty() || sausages.size() > 0)

&& (useOnionFlag.get() || hasMushroom(ENOKI, PORTOBELLO)) && hasCheese()) {

&& (useOnionFlag.get() || hasMushroom(ENOKI, PORTOBELLO)) && hasCheese()) {

...

}

A first step toward improving this is to extract the condition into a well-named variable: 改善这一点的第一步是将条件提取到一个命名良好的变量中:

boolean isPizzaFantastic =

(!pepperoniService.empty() || sausages.size() > 0)

(!pepperoniService.empty() || sausages.size() > 0)

&& (useOnionFlag.get() || hasMushroom(ENOKI, PORTOBELLO)) && hasCheese();

&& (useOnionFlag.get() || hasMushroom(ENOKI, PORTOBELLO)) && hasCheese();

if (isPizzaFantastic) { if (isPizzaFantastic) {

...

}

However, the Boolean expression is still too complex. It's potentially confusing to calculate the value of isPizzaFantastic from a given set of inputs. You might need to grab a pen and paper, or start a server locally and set breakpoints. 但是,布尔表达式仍然过于复杂。从一组给定的输入中计算 isPizzaFantastic 的值可能会令人困惑您可能需要拿起笔和纸,或者在本地启动服务器并设置断点。

Instead, try to group the details into intermediate Booleans that provide meaningful abstractions. Each Boolean below represents a single well-defined quality, and you no longer need to mix && and || within an expression. Without changing the business logic, you’ve made it easier to see how the Booleans relate to each other: 相反,尝试将详细信息分组到中间布尔值中,以提供有意义的抽象。下面的每个布尔值都代表一个明确定义的质量,您不再需要混合 && 和 || 在表达式中。在不更改业务逻辑的情况下,您可以更轻松地查看布尔值之间的关系:

boolean hasGoodMeat = !pepperoniService.empty() || sausages.size() > 0; boolean hasGoodMeat = !pepperoniService.empty() ||sausages.size() > 0;

boolean hasGoodVeggies = useOnionFlag.get() || hasMushroom(ENOKI, PORTOBELLO); boolean hasGoodVeggies = useOnionFlag.get() ||hasMushroom(榎树、波多贝罗);

boolean isPizzaFantastic = hasGoodMeat && hasGoodVeggies && hasCheese(); boolean isPizzaFantastic = hasGoodMeat && hasGoodVeggies && hasCheese();

Another option is to hide the logic in a separate method. This also offers the possibility of early returns using guard clauses, further reducing the need to keep track of intermediate states: 另一种选择是在单独的方法中隐藏逻辑。这也提供了使用保护条款提前返回的可能性,进一步减少了跟踪中间状态的需要:

boolean isPizzaFantastic() { 布尔值 isPizzaFantastic() {

if (!hasCheese()) { 如果 (!hasCheese()) {

return false; 返回 false;

}

if (pepperoniService.empty() && sausages.size() == 0) { if (pepperoniService.empty() && sausages.size() == 0) {

return false; 返回 false;

}

return useOnionFlag.get() || hasMushroom(ENOKI, PORTOBELLO); 返回 useOnionFlag.get() ||hasMushroom(榎树、波多贝罗);}