refactoring conditionals lesson five: conditionals
TRANSCRIPT
RefactoringConditionals
Lesson Five: Conditionals
RefactoringConditionals
Conditionals should not be too complex. If they are complex, refactor them to make simple methods making readability and understandability better.
RefactoringConditionals
Learning objective – have simple conditionals which are self documenting with meaningful names
RefactoringConditionals
In our program with explaining variable names, there are still some conditionals which are difficult to understand. We find them in mainly in bestMove().
RefactoringConditionals
FOR EXAMPLEint bestMove(int computerStatus, int userStatus) {
…… if (((computerStatus & (1 <<
potentialComputerMove)) == 0) && ((userStatus & (1 << potentialComputerMove)) == 0))
if (((potentialComputerStatus & (1 << j)) == 0) && ((userStatus & (1 << j)) == 0))
if (((computerStatus & (1 <<firstAvailableComputerMove)) == 0) && ((userStatus & (1 <firstAvailableComputerMove)) == 0)) {
RefactoringConditionals
REFACTORINGS
Decomposing ConditionalConsolidating Conditional Statements Consolidate Duplicate Conditional FragmentsReplace Nested Conditional with Guard ClausesRemove Control Flag
RefactoringConditionals
Summary:
You have a complicated conditional (if-then-else) statement.
Extract methods from the condition, then part, and else parts.
Decompose Conditional
RefactoringConditionals
Decompose Conditional:
Motivation:
Complexity exist in a heavy if then else structure and you want to make the code more readable and understandable.
You extract methods and name them where the code is readable and make small boolean methods to make the logic more understandable.
RefactoringConditionals
Decompose Conditional: if (date.before (SUMMER_START) || date.after(SUMMER_END))
charge – qualitity * _winterRate + _winterServiceCharge;
else charge – quantity * _summerRate;
if (notSummer_date))
charge = winterCharge(quantity);
else charge = summerCharge (quantity);
GOES TO
RefactoringConditionals
Decompose Conditional:
if (date.before (SUMMER)START) || date.after(SUMMER_END))
charge – qualitity * _winterRate + _winterServiceCharge;
else charge – quantity * _summerRate;
Consider the following initial if statement.
RefactoringConditionals
Decompose Conditional:
If(notSummer(date) charge = winterCharge(quantity);
else charge = summerCharge (quantity);
private boolean notSummer (Date date) {
return date.before (SUMMER_START) || date.after (SUMMER_END);
} // end not summer
private double summerCharge (int quantity) {
return quantity * _summerRate;
} // end summercharge
private double winterCharge (int quantity) {
return quantity * _winterRate + )winterServiceCharge;
}
Extract the conditional and each leg as follows.
RefactoringConditionals
Decompose Conditional:
Mechanics:
Extract the condition into its own method
Extract the then part then the else part
Compile and test
RefactoringConditionals
Summary:
You have a sequence of conditional tests with the same result.
Combine them into a single conditional expression and extract it.
Consolidate Conditional Expressions
RefactoringConditionals
Consolidate Conditional Expressions:
Motivation:
Sometimes you have a series of conditional checks in which the conditional statements are different but the effects are the same.
RefactoringConditionals
Consolidate Conditional Expressions:
double disabilityAmount() {
if (_seniority < 2) return 0;
if (_monthsDisabled > 12) return 0;
if (_isPartTime) return 0;
// compute the disability amount
Double diabilityAmount () {
if (isNOtEligableForDisability ()) return 0;
// computer the disability amount
GOES TO.
RefactoringConditionals
Consolidate Conditional Expressions:
double disabilityAmount() {
if (_seniority < 2) return 0;
if (_monthsDisabled > 12) return 0;
if (_isPartTime) return 0;
// compute the disability amount
double diabilityAmount () {
if (_seniority < 2) || (_monthsDisabled > 12) || (_isPartTime)) return 0;
// computer the disability amount
Assume the following.
These statements may be equivalent of a complex or statement
RefactoringConditionals
Consolidate Conditional Expressions:
double disabilityAmount() {
if (isEligibleForDiability()) return 0;
// computer the disability amount
Boolean isEligibleForDisability() {
return ((_seniority < 2) || (_monthsDisabled > 12) || (_isPartTime));
} // end is ElibibleFor Disability
Look at the condition and use extract method to communicate what the condition is looking for.
RefactoringConditionals
Consolidate Conditional Expressions:
if (onVacation())
if (lengthOfService*( > 10 ) return 1;
return 0.5;
if (onVacation () && lengthOfService () > 10) return 1;
else return 0.5;
This can be done with ands.
RefactoringConditionals
Consolidate Conditional Expressions:
if (onVacation() && lengthOfService*( > 10 ) return 1;
return 0.5;
return (onVacation () && lengthOfService () > 10) ? 1: 0.5;
Given the following
Test only the condition and returns a value turn the routine into a single return statements using the ternary operator.
RefactoringConditionals
Consolidate Conditional Expressions:
Mechanics:
Check that none of the conditional has side effects.
Replace the string of conditionals with a single conditional statement using logical operators.
Compile and test.
Consider using Extract Method on the condition.
RefactoringConditionals
Summary:
The same fragment of code is in all branches of a conditional expression.
Move it outside of the expression.
Consolidate Duplicate Conditional Fragments
RefactoringConditionals
Consolidate Duplicate Conditional Fragments:
Motivation:
Sometime you find the same code executed in all legs of a conditional. This requires you move the code outside the conditional to make it more clear what remains constant.
RefactoringConditionals
Consolidate Duplicate Conditional Fragments:
if (isSpecialDeal() ) { total = price * 0.95; send (); } // end if
else { total – price * 0.98; send(); } // end else
if (isSpecialDeal() ) total = price * 0.95;
else total =price * 0.98;
send();
You begin with the following
Move the send method out since it is used by both
RefactoringConditionals
Consolidate Duplicate Conditional Fragments:
Mechanics:
Identify code that is executed.
If common code is at the beginning, do it before conditional.
If the code is at the end, move it after the conditional
If the common code is in the middle, see if you can move it forward or backward.
If there is more than one line of code extract method.
RefactoringConditionals
Summary:
You have a variable that is acting as a control flag for a series of boolean expressions.
Use a break or return instead.
Remove Control Flag
RefactoringConditionals
Remove Control Flag:
Motivation:
You have a control flag that allows exit from the conditional statements. These control flags add complexity. Get rid of the control flag.
RefactoringConditionals
Remove Control Flag:
void checkSecurity (String [ ] people) {
boolean found = false;
for (int i = 0; i < people.length; i++) {
if (!found) { // this is the flag
if (people[i].equals (“Don”)) { sendAlert(); found = true; }
if (people[i].equals (“John”)) { sendAlert(); found = true; }
} // end if
} // end for
} // end checkSecurity
Function checks a people list for suspicious characters
RefactoringConditionals
Remove Control Flag:
void checkSecurity (String [ ] people) {
boolean found = false;
for (int i = 0; i < people.length; i++) {
if (!found) {
if (people[i].equals (“Don”))
{ sendAlert(); found = true; break; }
if (people[i].equals (“John”))
{ sendAlert(); found = true; break; }
} // end if
} // end for
} // end checkSecurity
The control flag is the piece that sets the found variable.Step 1: Set breaks in one at a time. Leave in flag.
RefactoringConditionals
Remove Control Flag:
void checkSecurity (String [ ] people) {
for (int i = 0; i < people.length; i++) {
if (people[i].equals (“Don”))
{ sendAlert(); break; }
if (people[i].equals (“John”))
{ sendAlert(); break; }
} // end for
} // end checkSecurity
Step 2: Remove control flag references.
RefactoringConditionals
Remove Control Flag:
String checkSecurity (String [ ] people) {
for (int i = 0; i < people.length; i++) {
if (people[i].equals (“Don”))
{ sendAlert(); return “Don”; }
if (people[i].equals (“John”))
{ sendAlert(); return “John”; }
} // end for
return “”; // returns null if neither
} // end checkSecurity
You can use the return statement with function returning a String.
RefactoringConditionals
Remove Control Flag:
Mechanics:
Find the value of the control flag that gets you out of the logic statement.
Replace assignments of the break-out value with a break or continue statement.
Compile and test after each replacement.
RefactoringConditionals
Summary:
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
RefactoringConditionals
Replace Nested Conditional with Guard Clauses:
Motivation:
Expressional statement may be of two forms,
One form is whether either course is part of the normal behavior
And the second form is a situation in which one answer from the conditional indicates normal behavior and the other indicates an unusual condition.
RefactoringConditionals
Replace Nested Conditional with Guard Clauses:
Motivation: (con’t)
Each type of conditions have different intensions.
If both are part of normal behavior, use a condition with an if and an else leg.
If the condition is a unusual condition, check the condition and return if the condition is true.
This is called a guard clause.
RefactoringConditionals
Replace Nested Conditional with Guard Clauses:
double getPayAmount() {
double result;
if (_isDead) result = deathAmount(); // this is NOT a normal course
else {
if (_isSeparated) result = separatedAmount(); // this is a normal course
else { if (_isRetired) result = retiredAmount(); // this is a normal course
else result = normalPayAmount(); // this is the normal course
} // end else
} // end else
return result
} // end getPayAmount
Given
RefactoringConditionals
Replace Nested Conditional with Guard Clauses:
double getPayAmount() {
if (_isDead) return deadAmount();
if (_isSeparated) return separatedAmount();
if (_isRetired) return retiredAmount();
return normalPayAmount();
} // end getPayAmount
GOES TO separating out the behaviors.
RefactoringConditionals
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();
} // end else
} // end else
return result
} // end getPayAmount
Given this initial code as shown before -- this is not unlikely code
RefactoringConditionals
Replace Nested Conditional with Guard Clauses:
double getPayAmount() {
double result;
if (_isDead) return deadAmount(); // this is masking of normals
else {
if (_isSeparated) result = separatedAmount();
else { if (_isRetired) result = retiredAmount(); else result = normalPayAmount();
} // end else
} // end else
return result
} // end getPayAmount
Checking masking the normal course of action behind the checking. So we use a guard clause one at a time.
RefactoringConditionals
Replace Nested Conditional with Guard Clauses:
Mechanics:
For each check put in the guard clause.
Compile and test after each check is replaced with a guard clause.
RefactoringConditionals
Summary:
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.
Replace Conditional with Polymorphism
RefactoringConditionals
Replace Conditional with Polymorphism:
Motivation:
Avoid writing explicit conditional statements when you have objects whose behavior varies depending on the type of object you are dealing with.
Polymorphism gives you the advantage.
RefactoringConditionals
Replace Conditional with Polymorphism:
double getSpeed() {
switch ()type) {
case EUROPEAN: return getBaseSpeed();
case AFRICAN: return getBaseSpeed() – getLoadFactor() * _numberOfCoconuts;
case NORWEGIAN_BLUE: return (_isNailed) ? 0 ; getBaseSpeed (_voltage);
} // end switch
throw new RuntimeException (“Should be unreachable”);
} // end getSpeed
Given the following speeds for different types of birds
RefactoringConditionals
Replace Conditional with Polymorphism:
GOES TO Bird____________getSpeed
Bird____________getSpeed
Bird____________getSpeed
Bird____________getSpeed
RefactoringConditionals
Replace Conditional with Polymorphism:
Method:
1. Use Extract Method to take apart the conditional.
2. Use either Replace Type Code with SubClasses OR
Use Replace Type Code with State/Strategy
3. Use Move Method to place condition in an isa structure.
4. Create sub-classes with overriding methods.
RefactoringConditionals
Replace Conditional with Polymorphism:
Suppose you have the following inheritance structure
Employee Type
ENGINEER SALESMAN MANAGER
Employee
RefactoringConditionals
Replace Conditional with Polymorphism:
class Employee..
int payAmount() {
switch (getType)() {
case EmployeeType.ENGINEER: return _monthlySalary;
case EmployeeType.SALESMAN: return _monthlySalary + commission;
case EmployeeType.MANAGER: return _monthlySalary + bonus;
default;
throw new RuntimeException (“Incorrect Employee”);
} // end switch
} // end Employee
With the following code
RefactoringConditionals
Replace Conditional with Polymorphism:
int getType () { return _type.getTypeCode(); } // returns type of object
private EmployeeType _type; // an instance of employee type in the program
abstract class EmployeeType…. // is the abstract class of employee type
abstract int getTypeCode();
class Engineer extends EmployeeType… // defines the subclass
int getTypeCode() { return Employee.ENGINEER; } // this returns the type
With the following code
RefactoringConditionals
Replace Conditional with Polymorphism:
class EmployeeType… // this code used to be in Employee
int payAmount (Employee emp) {
switch (getTypeCode ()) {
case ENGINEER: return emp.getMonthlySalary ();
case SALESMAN: return emp.getMonthlySalary () + emp.getCommission();
case MANAGER: return emp.getMonthlySalary() + emp.getBonus();
default: throw new RuntimeException (“Incorrect Employee”);
The case statement is extracted so I move it into employee type.
RefactoringConditionals
Replace Conditional with Polymorphism:
class Employee…
int payAmount () {
return _type.payAmount (this))
class Engineer…
int payAmount (Employee emp) { return emp.getMonthlySalary(); }
Change the payAmount method to delegate to the new class.
Work on Case statement – copy engineer leg to engineer class
RefactoringConditionals
Replace Conditional with Polymorphism:
class EmployeeType…
int payAmount (Employee emp) {
switch (getTypeCode ()) {
case ENGINEER: throw new RuntimeException (“Should be being overridden”);
case SALESMAN: return emp.getMonthlySalary () + emp.getCommission();
case MANAGER: return emp.getMonthlySalary() + emp.getBonus();
default: throw new RuntimeException (“Incorrect Employee”);
The new method overrides the case statement for engineers
I can place a trap here to make sure I made no mistakes
RefactoringConditionals
Replace Conditional with Polymorphism:
class Employee…
int payAmount () {
return _type.payAmount (this))
class Engineer…
int payAmount (Employee emp) { return emp.getMonthlySalary(); }
I can then replace this switch statement entirely
With a polymorphic call.
RefactoringConditionals
Replace Conditional with Polymorphism:
Mechanics:
If the conditional statement is one part of a larger method, take apart the conditional statement and use Extract Method.
If necessary use Move method to place the conditional at the top of the inheritance structure.
Pick one of the subclasses.
Remove the copied leg of the conditional statement.
Compile and test.
RefactoringConditionals
if (((computerStatus & (1 << firstAvailableComputerMove)) == 0) && ((userStatus & (1 << firstAvailableComputerMove)) == 0))
public boolean cellEmpty (int computerStatus,int potentialComputerMove,int userStatus) { return ((computerStatus & (1 << potentialComputerMove)) == 0) && ((userStatus & (1 << potentialComputerMove)) == 0); } // end cellEmpty
BECOMES
RefactoringConditionals
if ((getComputerStatus() & (1 << i)) != 0)
public boolean squareOccupied (int i, int playerStatus) { return ((playerStatus & (1 << i)) != 0); } // end squareOccupied
BECOMES
WITH
if (squareOccupied (i, getComputerStatus()))
REUSED 3 times