1.1. Decompose Conditional ¶
- You have a complicated conditional (if-then-else) statement.
Extract methods from the condition, then part, and else parts.
~cpp if (data.before( SUMMER_START ) || data.after(SUMMER_END) ) charge = quantity * _winterRate + _winterServeceCharge; else charge = quantity * _summerRate;
~cpp if (notSummer(date)) charge = winterCharge(quantity); else charge = summerCharge(quatity);
1.2. Consolidate Conditional Expression ¶
- You have a sequence of conditional tests with the same result.
Combine them into a single conditional expression and extract it.
~cpp double disabilityAmount() { if (_seniority < 2) return 0; if ( _monthsDisabled > 12) return 0; if ( _isPartTime) return 0; // compute the disability amount
~cpp double disabilityAmount() { if( isNotEligableForDisability()) return 0; // compute the disability amount;
1.3. Consolidate Duplicate Conditional Fragments ¶
- The same fragment of code is in all branches of a conditional expression.
Move it outside of the expression.
~cpp if (isSpecialDeal()){ total = price * 0.95; send(); } else { total = price * 0.98; send(); }
~cpp if (isSpecialDeal()) total = price * 0.95 else total = price * 0.98; send();
1.4. Replace Nested Conditional with Guard Clauses ¶
- A method has conditional behavior that does not make clear the normal path of execution
Use guard clauses for all the special cases.
~cpp double getPayAmount(){ double result; if( _isDead) result = deadAmount(); else { if (_isSeparated) result = separatedAmount(); else { if (_isRetried) result = retiredAmount(); else result = normalPayAmount(); }; } return result;
~cpp double getPayAmount() { if (_isDead) return deadAmount(); if (_isSeparated) return separatedAmount(); if (_isRetried) return retiredAmount(); return normalPayAmount();
1.5. Replace Conditional with Polymorphism ¶
- You have a conditional that chooses different behavior depending on the type of and object
Move each leg of the conditional to an overriding method in a subclass. Make the orginal method abstract.
~cpp double getSpeed() { switch (_type) { case EUROPEAN: return getBaseSpeed(); case AFRICAN: return getBaseSpeed() - getLoadFactor() * _numberofCoconuts; case NORWEGIAN_BLUE: return (_isNailed) ? 0 : getBaseSpeed(_voltage); } throw new RuntimeException ("Should be unreachable"); }
~cpp ┌─────┐ │ Bird │ ├─────┤ │getSpeed │ └─────┘ △ │ ┌───────┼─────────┐ │ │ │ ┌──┴──┐ ┌──┴──┐ ┌────┴──┐ │European| │ │African │ │Norwegian Blue│ ├─────┤ ├─────┤ ├───────┤ │getSpeed │ │getSpeed │ │getSpeed │ └─────┘ └─────┘ └───────┘
1.6. Introduce Null Object ¶
- You have repeated checks for a null value
Replace the null value with a null object.
~cpp if (customer == null) plan = BillingPlan.basic(); else plan = customer.getPlan();
~cpp ┌───────┐ │ Cutomer │ ├───────┤ │getPlan │ └───────┘ △ │ ┌───┴───┐ │Null Customer │ ├───────┤ │getPlan │ └───────┘
1.7. Introduce Assertion ¶
- A section of code assumes something about the state of the program.
Make the assumption explicit with an assertion.
~cpp double getExpenseLimit() { //should have eigher expense limit or a primary project return (_expenseLimit != NULL_EXPENSE)? _expenseLimit: _primaryProject.getMemberExpenseLimit(); }
~cpp double getExpenseLimit() { Assert.isTrue( _expenseLimit != NULL_EXPENSE || _primaryProject != null ); return (_expenseLimit != NULL_EXPENSE)? _expenseLimit: _primaryProject.getMemberExpenseLimit();
Refactoring