Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to simplify complicated business "IF" logic?

What are the good ways to handle complicated business logic that from the first glance requires many nested if statements?

Example:

Discount Coupon. could be:

1a) Value discount
1b) Percentage discount

2a) Normal discount
2b) Progressive discount

3a) Requires access coupon
3b) Do not require access coupon

4a) Applied only to the customer who already bought before
4b) Applied to any customer

5a) Applied to customer only from countries (X,Y,…)

That requires code even more complicated then this:

if (discount.isPercentage) {
    if (discount.isNormal) {
        if (discount.requiresAccessCoupon) {
        } else {
        }
    } else if (discount.isProgressive) {
        if (discount.requiresAccessCoupon) {
        } else {
        }
    }
} else if (discount.isValue) {
    if (discount.isNormal) {
        if (discount.requiresAccessCoupon) {
        } else {
        }
    } else if (discount.isProgressive) {
        if (discount.requiresAccessCoupon) {
        } else {
        }
    }
} else if (discount.isXXX) {
    if (discount.isNormal) {
    } else if (discount.isProgressive) {
    }
}

Even if you replace IFs to switch/case it's still too complicated. What are the ways to make it readable, maintainable, more testable and easy to understand?

like image 383
Zelid Avatar asked Oct 22 '09 13:10

Zelid


1 Answers

Good question. "Conditional Complexity" is a code smell. Polymorphism is your friend.

Conditional logic is innocent in its infancy, when it’s simple to understand and contained within a few lines of code. Unfortunately, it rarely ages well. You implement several new features and suddenly your conditional logic becomes complicated and expansive. [Joshua Kerevsky: Refactoring to Patterns]

One of the simplest things you can do to avoid nested if blocks is to learn to use Guard Clauses.

double getPayAmount() {
if (_isDead) return deadAmount();
if (_isSeparated) return separatedAmount();
if (_isRetired) return retiredAmount();
return normalPayAmount();
};  

The other thing I have found simplifies things pretty well, and which makes your code self-documenting, is Consolidating conditionals.

double disabilityAmount() {
    if (isNotEligableForDisability()) return 0;
    // compute the disability amount

Other valuable refactoring techniques associated with conditional expressions include Decompose Conditional, Replace Conditional with Visitor, and Reverse Conditional.

like image 128
Ewan Todd Avatar answered Sep 22 '22 01:09

Ewan Todd