refactoring conditionals lesson five: conditionals

54
Refactoring Conditionals Lesson Five: Conditionals

Upload: meredith-marsh

Post on 17-Jan-2016

300 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Refactoring Conditionals Lesson Five: Conditionals

RefactoringConditionals

Lesson Five: Conditionals

Page 2: Refactoring Conditionals 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.

Page 3: Refactoring Conditionals Lesson Five: Conditionals

RefactoringConditionals

Learning objective – have simple conditionals which are self documenting with meaningful names

Page 4: Refactoring Conditionals Lesson Five: Conditionals

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().

Page 5: Refactoring Conditionals Lesson Five: Conditionals

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)) {

Page 6: Refactoring Conditionals Lesson Five: Conditionals

RefactoringConditionals

REFACTORINGS

Decomposing ConditionalConsolidating Conditional Statements Consolidate Duplicate Conditional FragmentsReplace Nested Conditional with Guard ClausesRemove Control Flag

Page 7: Refactoring Conditionals Lesson Five: Conditionals

RefactoringConditionals

Summary:

You have a complicated conditional (if-then-else) statement.

Extract methods from the condition, then part, and else parts.

Decompose Conditional

Page 8: Refactoring Conditionals Lesson Five: Conditionals

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.

Page 9: Refactoring Conditionals Lesson Five: Conditionals

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

Page 10: Refactoring Conditionals Lesson Five: Conditionals

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.

Page 11: Refactoring Conditionals Lesson Five: Conditionals

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.

Page 12: Refactoring Conditionals Lesson Five: Conditionals

RefactoringConditionals

Decompose Conditional:

Mechanics:

Extract the condition into its own method

Extract the then part then the else part

Compile and test

Page 13: Refactoring Conditionals Lesson Five: Conditionals

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

Page 14: Refactoring Conditionals Lesson Five: Conditionals

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.

Page 15: Refactoring Conditionals Lesson Five: Conditionals

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.

Page 16: Refactoring Conditionals Lesson Five: Conditionals

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

Page 17: Refactoring Conditionals Lesson Five: Conditionals

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.

Page 18: Refactoring Conditionals Lesson Five: Conditionals

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.

Page 19: Refactoring Conditionals Lesson Five: Conditionals

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.

Page 20: Refactoring Conditionals Lesson Five: Conditionals

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.

Page 21: Refactoring Conditionals Lesson Five: Conditionals

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

Page 22: Refactoring Conditionals Lesson Five: Conditionals

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.

Page 23: Refactoring Conditionals Lesson Five: Conditionals

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

Page 24: Refactoring Conditionals Lesson Five: Conditionals

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.

Page 25: Refactoring Conditionals Lesson Five: Conditionals

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

Page 26: Refactoring Conditionals Lesson Five: Conditionals

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.

Page 27: Refactoring Conditionals Lesson Five: Conditionals

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

Page 28: Refactoring Conditionals Lesson Five: Conditionals

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.

Page 29: Refactoring Conditionals Lesson Five: Conditionals

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.

Page 30: Refactoring Conditionals Lesson Five: Conditionals

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.

Page 31: Refactoring Conditionals Lesson Five: Conditionals

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.

Page 32: Refactoring Conditionals Lesson Five: Conditionals

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

Page 33: Refactoring Conditionals Lesson Five: Conditionals

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.

Page 34: Refactoring Conditionals Lesson Five: Conditionals

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.

Page 35: Refactoring Conditionals Lesson Five: Conditionals

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

Page 36: Refactoring Conditionals Lesson Five: Conditionals

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.

Page 37: Refactoring Conditionals Lesson Five: Conditionals

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

Page 38: Refactoring Conditionals Lesson Five: Conditionals

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.

Page 39: Refactoring Conditionals Lesson Five: Conditionals

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.

Page 40: Refactoring Conditionals Lesson Five: Conditionals

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

Page 41: Refactoring Conditionals Lesson Five: Conditionals

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.

Page 42: Refactoring Conditionals Lesson Five: Conditionals

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

Page 43: Refactoring Conditionals Lesson Five: Conditionals

RefactoringConditionals

Replace Conditional with Polymorphism:

GOES TO Bird____________getSpeed

Bird____________getSpeed

Bird____________getSpeed

Bird____________getSpeed

Page 44: Refactoring Conditionals Lesson Five: Conditionals

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.

Page 45: Refactoring Conditionals Lesson Five: Conditionals

RefactoringConditionals

Replace Conditional with Polymorphism:

Suppose you have the following inheritance structure

Employee Type

ENGINEER SALESMAN MANAGER

Employee

Page 46: Refactoring Conditionals Lesson Five: Conditionals

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

Page 47: Refactoring Conditionals Lesson Five: Conditionals

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

Page 48: Refactoring Conditionals Lesson Five: Conditionals

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.

Page 49: Refactoring Conditionals Lesson Five: Conditionals

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

Page 50: Refactoring Conditionals Lesson Five: Conditionals

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

Page 51: Refactoring Conditionals Lesson Five: Conditionals

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.

Page 52: Refactoring Conditionals Lesson Five: Conditionals

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.

Page 53: Refactoring Conditionals Lesson Five: Conditionals

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

Page 54: Refactoring Conditionals Lesson Five: Conditionals

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