Refactoring Conditionals Lesson Five: Conditionals
Refactoring Conditionals Conditionals should not be too complex. If they are complex, refactor them to make simple methods making readability and understandability better.
Refactoring Conditionals Learning objective – have simple conditionals which are self documenting with meaningful names
Refactoring Conditionals In our program with explaining variable names, there are still some conditionals which are difficult to understand. We find them in mainly in bestMove().
Refactoring Conditionals FOR EXAMPLE int 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)) {
Refactoring Conditionals REFACTORINGS Decomposing Conditional Consolidating Conditional Statements Consolidate Duplicate Conditional Fragments Replace Nested Conditional with Guard Clauses Remove Control Flag
Refactoring Conditionals Summary: You have a complicated conditional (if-then-else) statement. Extract methods from the condition, then part, and else parts. Decompose Conditional
Refactoring Conditionals 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.
Refactoring Conditionals 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
Refactoring Conditionals 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.
Refactoring Conditionals 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.
Refactoring Conditionals Decompose Conditional: Mechanics: Extract the condition into its own method Extract the then part then the else part Compile and test
Refactoring Conditionals 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
Refactoring Conditionals 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.
Refactoring Conditionals 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.
Refactoring Conditionals 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 12) || (_isPartTime)) return 0; // computer the disability amount Assume the following. These statements may be equivalent of a complex or statement
Refactoring Conditionals Consolidate Conditional Expressions: double disabilityAmount() { if (isEligibleForDiability()) return 0; // computer the disability amount Boolean isEligibleForDisability() { return ((_seniority 12) || (_isPartTime)); } // end is ElibibleFor Disability Look at the condition and use extract method to communicate what the condition is looking for.
Refactoring Conditionals 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.
Refactoring Conditionals 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.
Refactoring Conditionals 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.
Refactoring Conditionals Summary: The same fragment of code is in all branches of a conditional expression. Move it outside of the expression. Consolidate Duplicate Conditional Fragments
Refactoring Conditionals 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.
Refactoring Conditionals 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
Refactoring Conditionals 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.
Refactoring Conditionals 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
Refactoring Conditionals 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.
Refactoring Conditionals 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
Refactoring Conditionals 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.
Refactoring Conditionals 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.
Refactoring Conditionals 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.
Refactoring Conditionals 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.
Refactoring Conditionals 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
Refactoring Conditionals 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.
Refactoring Conditionals 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.
Refactoring Conditionals 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 return result } // end getPayAmount Given
Refactoring Conditionals 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.
Refactoring Conditionals 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 return result } // end getPayAmount Given this initial code as shown before -- this is not unlikely code
Refactoring Conditionals 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 return result } // end getPayAmount Checking masking the normal course of action behind the checking. So we use a guard clause one at a time.
Refactoring Conditionals 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.
Refactoring Conditionals 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
Refactoring Conditionals 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.
Refactoring Conditionals 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
Refactoring Conditionals Replace Conditional with Polymorphism: GOES TO Bird ____________ getSpeed Bird ____________ getSpeed Bird ____________ getSpeed Bird ____________ getSpeed
Refactoring Conditionals 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.
Refactoring Conditionals Replace Conditional with Polymorphism: Suppose you have the following inheritance structure Employee Type ENGINEER SALESMAN MANAGER Employee
Refactoring Conditionals 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
Refactoring Conditionals 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
Refactoring Conditionals 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.
Refactoring Conditionals 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
Refactoring Conditionals 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
Refactoring Conditionals 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.
Refactoring Conditionals 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.
Refactoring Conditionals 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
Refactoring Conditionals 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