[[TableOfContents]] = Chapter 9 Simplifying Conditional Expressions = == Decompose Conditional == * You have a complicated conditional (if-then-else) statement. [[BR]] ''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); }}} == Consolidate Conditional Expression == * You have a sequence of conditional tests with the same result. [[BR]]''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; }}} == Consolidate Duplicate Conditional Fragments == * The same fragment of code is in all branches of a conditional expression. [[BR]]''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(); }}} == Replace Nested Conditional with Guard Clauses == * A method has conditional behavior that does not make clear the normal path of execution [[BR]] ''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(); }}} == Replace Conditional with Polymorphism == * You have a conditional that chooses different behavior depending on the type of and object [[BR]] ''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 │ └─────┘ └─────┘ └───────┘ }}} == Introduce Null Object == * You have repeated checks for a null value[[BR]] ''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 │ └───────┘ }}} == Introduce Assertion == * A section of code assumes something about the state of the program. [[BR]]''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"]