This article is shared with huawei cloud community “Replace Nested Conditional expressions with Guard Clauses” by JavaEdge.

motivation

Conditional expressions usually come in two styles:

  • Both conditional branches are normal behavior
  • Only one branch of the condition is normal behavior, and the other branch is abnormal condition

These two types of conditional expressions have different uses, which should be shown in code:

  • If both branches are normal behavior, use something like if… The else… Conditional expression of

  • If a condition is extremely rare, it should be checked separately and returned from the function as soon as it is true

    Such separate checks are often referred to as guard clauses.

The essence of using statements instead of nested conditional expressions is to give particular weight to one branch. If you use if-then-else, you give equal weight to the if branch as to the else branch. The message this code structure conveys to the reader is that all branches are equally important. The wei statement is telling the reader, “This situation is not the concern of the core logic of this function. If it does happen, do the necessary cleanup and exit.”

The idea that every function can only have one entry and one exit is ingrained in some programmers. When I work with the code they write, I often need to use a string statement instead of a nested conditional expression. Today’s programming languages enforce a single entry for each function, and the “single exit” rule is not that useful. Keeping the code clean is key: if a single exit makes the function more legible, use a single exit; Otherwise not.

practice

Select the outermost conditional logic that needs to be replaced and replace it with a guard statement.

The test.

Repeat the steps as necessary.

If all statements raise the same result, you can use merge condition expressions to merge them.

case

Calculate the wages to be paid to employees. Only employees who still work for the company need to be paid, so this function needs to check for two “employee no longer works for the company” cases.

    public Long payAmount(Employee employee) {
        long result;
        if (employee.isSeparated) {
            result = 0;
        } else {
            if (employee.isRetired) {
                result = 0;
            } else {
                // logic to compute amount
                lorem.ipsum(dolor.sitAmet);
                consectetur(adipiscing).elit();
                sed.do.eiusmod = tempor.incididunt.ut(labore) && dolore(magna.aliqua);
                ut.enim.ad(minim.veniam);
                result = someFinalComputation();
            }
        } return result;
    }
Copy the code

Nested conditional logic makes it hard to see what the code really means. Only when neither of the current two conditional expressions is true does the code really begin its main work. As a result, it makes it clear what the code is trying to do. As always, I like to take small steps, so I’ll deal with the top conditional logic first.

    public Long payAmount(Employee employee) {
        long result;
        if (employee.isSeparated) {
            result = 0;
        }
        if (employee.isRetired) {
            result = 0;
        } else { // logic to compute amount
            lorem.ipsum(dolor.sitAmet);
            consectetur(adipiscing).elit();
            sed.do.eiusmod = tempor.incididunt.ut(labore) && dolore(magna.aliqua);
            ut.enim.ad(minim.veniam);
            result = someFinalComputation();
        } return result;
    }
Copy the code

After this change, I run the test and move on to the next step.

    public Long payAmount(Employee employee) {
        long result;
        if (employee.isSeparated) {
            return 0l;
        }
        if (employee.isRetired) {
            return 0l;
        }

        lorem.ipsum(dolor.sitAmet);
        consectetur(adipiscing).elit();
        sed. do.eiusmod = tempor.incididunt.ut(labore) && dolore(magna.aliqua);
        ut.enim.ad(minim.veniam);
        result = someFinalComputation();
        return result;
    }
Copy the code

At this point, the result variable is no longer useful, so I delete it:

    public Long payAmount(Employee employee) {
        if (employee.isSeparated) {
            return 0l;
        }
        if (employee.isRetired) {
            return 0l;
        }
        lorem.ipsum(dolor.sitAmet);
        consectetur(adipiscing).elit();
        sed. do.eiusmod = tempor.incididunt.ut(labore) && dolore(magna.aliqua);
        ut.enim.ad(minim.veniam);
        return someFinalComputation();
    }
Copy the code

It’s always nice to have one less variable.

Reverse the condition

It is often possible to reverse a conditional expression to replace a nested conditional expression with a statement.

public int adjustedCapital(Instrument anInstrument) {
  int result = 0;
  if (anInstrument.capital > 0) {
    if (anInstrument.interestRate > 0 && anInstrument.duration > 0) {
      result = (anInstrument.income / anInstrument.duration) * anInstrument.adjustmentFactor;
    }
  }
  return result;
}
Copy the code

Replace one by one. But this time I need to reverse the conditions when inserting the guard statement:

public int adjustedCapital(Instrument anInstrument) {
  int result = 0;
  if (anInstrument.capital <= 0) {
    return result;
  }
  if (anInstrument.interestRate > 0 && anInstrument.duration > 0) {
    result = (anInstrument.income / anInstrument.duration) * anInstrument.adjustmentFactor;
  }
  return result;
}
Copy the code

The next condition is a little more complicated, so I reverse it in two steps. First add a logical non-operation:

public int adjustedCapital(Instrument anInstrument) { int result = 0; if (anInstrument.capital <= 0) { return result; } if (! (anInstrument.interestRate > 0 && anInstrument.duration > 0)) { return result; } result = (anInstrument.income / anInstrument.duration) * anInstrument.adjustmentFactor; return result; }Copy the code

But leaving a logical no in a conditional expression like this would make my head spin, so I’ll simplify it to:

public int adjustedCapital(Instrument anInstrument) {
  int result = 0;
  if (anInstrument.capital <= 0) {
    return result;
  }
  if (anInstrument.interestRate <= 0 || anInstrument.duration <= 0) {
    return result;
  }
  result = (anInstrument.income / anInstrument.duration) * anInstrument.adjustmentFactor;
  return result;
}
Copy the code

These two logical statements raise the same result, so I can use merge condition expressions to merge them:

public int adjustedCapital(Instrument anInstrument) {
  int result = 0;
  if (anInstrument.capital <= 0 || anInstrument.interestRate <= 0 || anInstrument.duration <= 0) {
    return result;
  }
  result = (anInstrument.income / anInstrument.duration) * anInstrument.adjustmentFactor;
  return result;
}
Copy the code

At this point, the result variable does two things: I initially set it to 0, representing the value returned when the guard statement was fired; And then you assign it the final result. I can remove this variable completely, avoid having to do two things with one variable, and eliminate one variable.

public int adjustedCapital(Instrument anInstrument) { if (anInstrument.capital <= 0 || anInstrument.interestRate <= 0 ||  anInstrument.duration <= 0) { return 0; } return (anInstrument.income / anInstrument.duration) * anInstrument.adjustmentFactor; }Copy the code

reference

  • “Refactoring”
  • The Way to Clean Architecture

Click to follow, the first time to learn about Huawei cloud fresh technology ~