Navs
Code Smells Catalog
Complicated Boolean Expression

Last Revision — April 19, 2022

2 Min Read


Complicated Boolean Expression

One should keep in mind that Boolean Expressions can be troublesome for some people (in other words, not as quick to comprehend as if it were an adequately named method instead). Wake reminds the reader of De Morgan Laws that could be applied to simplify the expression.

Wake also reminds us about guard checks, which could "peel off" complexity layers from the expression. [1] Interestingly, Robert Martin gives a minimum example in his Encapsulate Conditionals refactoring explanation [2], which could be a bit controversial but is yet another good perspective to look at the problem. Instead of simplifying the expression, he paid attention to the readable intention. As I have just mentioned, the example provided might be a bit controversial because it contains just one AND operation, which is not a Discrete Mathematics 2 type problem. Nevertheless, the point is valid: An appropriately named function or method is more comprehensible at a glance than whatever the boolean expression it is.

I have linked the smell to Obscured Intent as causes just because of all the cases of any if not thing.notDone() double negated by token and name expressions, wherever they are. An unnecessary Flag Argument Code Smell might imperceptibly impact the complexity of an expression.

Causation

Similar to Conditional Complexity - developers introduced new features, and instead of doing it cleanly, developers used a dirty method conditional method instead.

Problems:

Comprehensibility

It's easier to read declarative words than to computate logic statements.

Example

Smelly (Robert C. Martin)

if (timer.has_expired() and not timer.is_recurrent()):
    ...

Solution (Robert C. Martin)

if (should_be_deleted(timer)):
    ...

Smelly

def cook(ready: bool, bag: list):
    if (ready):
        if (['raspberry', 'apple', 'tomato'] in bag and ['carrot', 'spinach', 'garlic'] not in bag):
            ...

Solution

# "ready" extracted out of the function scope
def cook(bag: list):
    def hasFruit(container: list) -> bool:
        return ['raspberry', 'apple', 'tomato'] in container
    def hasVeggie(container: list) -> bool:
        return ['carrot', 'spinach', 'garlic'] in container

    if not hasFruit(bag):
        return
    if hasVeggie(bag):
        return
    ...

Refactoring

  • Introduce Explaining Method or Variable
  • Use Guard Clauses
  • Simplify Conditional

Sources
  • [1], [Origin] - William C. Wake, "Refactoring Workbook" (2004)
  • [2] - Robert C. Martin - "Clean Code: A Handbook of Agile Software Craftsmanship" (2008)