Module 7. Simplifying Conditional Expressions Course: Refactoring.
Transcript of Module 7. Simplifying Conditional Expressions Course: Refactoring.
Module 7. Simplifying Conditional Expressions
Course: Refactoring
Overview
Decompose Conditional
Consolidate Conditional Expression
Consolidate Duplicate Conditional Fragments
Remove Control Flag
Replace Nested Conditional with Guard Clauses
Replace Conditional with Polymorphism
Introduce Null Object
Introduce Assertion
Decompose Conditional
You have a complicated conditional (if-then-else) statement.
Extract methods from the condition, then part, and else parts.
Decompose Conditional
if (date.before (SUMMER_START) || date.after(SUMMER_END))
charge = quantity * _winterRate + _winterServiceCharge;
else
charge = quantity * _summerRate;
if (notSummer(date))
charge = winterCharge(quantity);
else
charge = summerCharge (quantity);
Decompose Conditional
Before:
complexity in a program lies in complex conditional logic a pretty long method
After:
make your intention clearer by decomposing doing this for
the conditional part
each of the alternatives highlight the reason for the branching
Decompose Conditional: Mechanism
Extract the condition into its own method.
Extract the then part and the else part into their own methods.
Consolidate Conditional Expression
You have a sequence of conditional tests with the same result.
Combine them into a single conditional expression and extract it.
Consolidate Conditional Expression
double disabilityAmount() {
if (_seniority < 2) return 0;
if (_monthsDisabled > 12) return 0;
if (_isPartTime) return 0;
// compute the disability amount
double disabilityAmount() {
if (isNotEligableForDisability()) return 0;
// compute the disability amount
Consolidate Conditional Expression
Before:
a series of conditional checks with the same result
After:
it makes the check clearer sets you up for Extract Method
Hit:
code already communicates your intention
Consolidate Conditional Expression: Mechanism
Check side effects.
A single conditional statement.
Compile and test.
Extract Method.
Consolidate Duplicate Conditional Fragments
The same fragment of code is in all branches of a conditional expression.
Move it outside of the expression.
Consolidate Duplicate Conditional Fragments
if (isSpecialDeal()) {
total = price * 0.95;
send();
}
else {
total = price * 0.98;
send();
}
if (isSpecialDeal())
total = price * 0.95;
else
total = price * 0.98;
send();
Consolidate Duplicate Conditional Fragments
Identify code.
Move it to before the conditional.
Move it to after the conditional.
Look code in the middle.
Extract that code into a method.
Remove Control Flag
You have a variable that is acting as a control flag for a series of boolean expressions.
Use a break or return instead.
done = false
while not done
if (condition)
do something
done = true
next step of loop
Remove Control Flag
Before:
more trouble rules of structured programming “one entry and one exit point”
After:
more exit point the conditional becomes so much more clear
Remove Control Flag: Mechanism
Find the value of the control flag.
A break or continue statement.
Compile and test.
Extract the logic into a method.
Find the value of the control flag.
Replace with a return.
Compile and test.
Return is better than break and continue
Control Flag vs Result value
Exercise
Remove Control Flag
Decompose Conditional else statements that are a double negative are
difficult to understand.
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.
Replace Nested Conditional with Guard Clauses
double getPayAmount() {
double result;
if (_isDead) result = deadAmount();
else {
if (_isSeparated) result = separatedAmount();
else { if (_isRetired) result = retiredAmount();
else result = normalPayAmount(); };
}
return result; };
double getPayAmount() {
if (_isDead) return deadAmount();
if (_isSeparated) return separatedAmount();
if (_isRetired) return retiredAmount();
return normalPayAmount();
};
Replace Nested Conditional with Guard Clauses
Before:
conditional expressions come in two formstwo part of the normal behavior normal behavior
unusual condition
After:
use guard clause [Beck] and return the key point about is one of emphasis if-then-else construct you are giving equal weight
Hint:
clarity is the key principle: if the method is clearer with one exit point, use one exit point; otherwise don't.
Replace Nested Conditional with Guard Clauses
For each check put in the guard clause.
Compile and test.
Expressions.
Exercise
Replace Nested Conditional with Guard Clauses
Reversing the Conditions
Replace Conditional with Polymorphism
You have a conditional that chooses different behavior depending on the type of an object.
Move each leg of the conditional to an overriding method in a subclass. Make the original method abstract.
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");
}
Replace Conditional with Polymorphism
Before:
difficult to add a new type high coupling
After:
use polymorphism
switch or if-then-else are much less common just create a new subclass for a new condition reduces the dependencies
Replace Conditional with Polymorphism
Introduce Null Object
You have repeated checks for a null value.
Replace the null value with a null object.
Introduce Null Object
if (customer == null)
plan = BillingPlan.basic();
else
plan = customer.getPlan();
Introduce Null Object
Before: ask object for what type it is invoking some behavior based on the answer got rid of huge amounts of procedural code
After: object does the right thing
Use: the missing Gemstone session use of null object is the missing bin things almost never blow up use Singleton pattern
Introduce Null Object
1. Create a subclass with isNull operation.
2. Compile.
3. Find all places and replace.
4. Change null with isNull.
5. Compile and test.
6. Look for if not null.
7. Override the operation.
8. Remove the condition check
9. Compile, and test.
Introduce Assertion
A section of code assumes something about the state of the program.
Make the assumption explicit with an assertion.
Introduce Assertion
double getExpenseLimit() {
// should have either expense limit or a primary project
return (_expenseLimit != NULL_EXPENSE) ? _expenseLimit : _primaryProject.getMemberExpenseLimit();
}
double getExpenseLimit() {
Assert.isTrue (_expenseLimit != NULL_EXPENSE || _primaryProject != null); return (_expenseLimit != NULL_EXPENSE) ?
_expenseLimit:
_primaryProject.getMemberExpenseLimit();
}
Introduce Assertion
Before :
assumptions can only be decoded by looking through an algorithm
After:
failure of an assertion indicates error should never be used by other usually are removed for production code important to signal something is an assertion communication and debugging aids help the reader understand the assumptions help catch bugs closer to their origin
Introduce Assertion: Mechanism
When you see that a condition is assumed to be true, add an assertion to state it.
Exercise
Null Object
Real Project
Review
Decompose Conditional
Consolidate Conditional Expression
Consolidate Duplicate Conditional Fragments
Remove Control Flag
Replace Nested Conditional with Guard Clauses
Replace Conditional with Polymorphism
Introduce Null Object
Introduce Assertion