Download presentation
Presentation is loading. Please wait.
1
Software Engineering Key Refactorings
Department of Computer Science Ben-Gurion university Based on slides of: Mira Balaban Department of Computer Science Ben-Gurion university F. Tip. IBM T J Watson Research Center.
2
Fowler’s Refactorings Catalogue (1)
Composing Methods Extract Method. Inline Method. Inline Temp. Replace Temp with Query. … Moving Features Between Objects. Move Method. Move Field. Extract Class. Inline Class. Organizing Data. Replace Type Code with Class. Replace type Code with Subclasses. Replace type Code with State/Strategy.
3
Fowler’s Refactorings Catalogue (2)
Simplifying Conditional Expressions. Replace Conditional with Polymorphism … Making Method Calls Simpler. Rename Method. Add Parameter. Remove Parameter. Introduce Parameter Object. Replace Constructor with Factory Method.
4
Fowler’s Refactorings Catalogue (3)
Dealing with Generalization. Pull Up Field. Pull Up Method. Extract SuperClass. Extract SubClass. Extract Interface. Form Template Method. Replace Inheritance with Delegation. … Big Refactorings Convert Procedural Design to Objects Separate Domain from Presentation.
5
Key Refactorings Extract Method, Inline Method.
Inline Temp, Replace Temp with Query. Replace Method with Method Object. Move Method, Move Field, Extract Class. Replace Type Code with Class, Replace Type Code with Subclasses, Replace Type Code with State/Strategy. Replace Conditional with Polymorphism
6
Extract Method Extracting a code fragment into a method goal:
enable reuse; avoid cut-and-paste programming make long methods shorter and more readable "If the code you want to extract is very simple, such as a single message or function call, you should extract it if the name of the new method will reveal the intention of the code in a better way. If you can't come up with a more meaningful name, don't extract the code. "
7
Extract Method: Condition
The extracted code is fully contained in an enclosing lexical block. The extracted code does not include a “return” statement. Consider : a. local variables to the source method b. parameters of the source method that occur in the extracted code, such that: used in source method following the extracted code. assigned in the extracted code at most one such variable or parameter
8
Extract Method: Transformation (1)
Two operations: Add method. Revise source and target methods code. Add a new method: Invent a new name in the enclosing lexical scope. Pick a name that describes what the method does copy extracted code from the source method into the new target method. Add the new method in the enclosing lexical scope.
9
Extract Method: Transformation (2)
Revise methods code: scan extracted code for variables local in scope to the source method only used in target method: declare in target method variables local in scope to the source method, or parameters of the source method For variables only: Declared and used outside the extracted code used in the extracted code pass in as parameters Declared and used in the extracted code Used outside the extracted code declare outside the extracted code Apply action (2).
10
Extract Method: Transformation (3)
Revise methods code: scan extracted code for (cont) variables local in scope to the source method, or parameters of the source method assigned in target method used in source method following the extracted code at most one variable can be modified return changed value Adapt return type of target method Extracted code replacement: Case 4: var = target method call. Otherwise: target method call.
11
Extract method example
void printOwing(){ Enumeration e = _orders.elements(); double outstanding = 0.0; // print banner System.out.println("*************************"); System.out.println("***** Customer Owes *****"); // calculate outstanding while (e.hasMoreElements()){ Order each = (Order)e.nextElement(); outstanding += each.getAmount(); } // print details System.out.println("name:" + _name); System.out.println("amount" + outstanding);
12
example (2) void printOwing() { Enumeration e = _orders.elements();
double outstanding = 0.0; printBanner(); // calculate outstanding while (e.hasMoreElements()) { Order each = (Order) e.nextElement(); outstanding += each.getAmount(); } // print details System.out.println("name:" + _name); System.out.println("amount" + outstanding); private void printBanner() { System.out.println("*************************"); System.out.println("***** Customer Owes *****");
13
example (3) void printOwing() { Enumeration e = _orders.elements(); double outstanding = 0.0; printBanner(); // calculate outstanding while (e.hasMoreElements()) { Order each = (Order) e.nextElement(); outstanding += each.getAmount(); } printDetails(outstanding); private void printDetails(double outstanding) { System.out.println("name:" + _name); System.out.println("amount" + outstanding); public void printBanner() { ... }
14
example (4a) void printOwing() { double outstanding = 0.0;
printBanner(); outstanding = getOutstanding(outstanding); printDetails(outstanding); } private double getOutstanding(double outstanding) { Enumeration e = _orders.elements(); while (e.hasMoreElements()) { Order each = (Order) e.nextElement(); outstanding += each.getAmount(); return outstanding; private void printDetails(double outstanding) { ... } private void printBanner() { ... } only used in target method: declare in target method
15
example (4b) void printOwing() { double outstanding = 0.0;
printBanner(); outstanding = getOutstanding(outstanding); printDetails(outstanding); } private double getOutstanding(double outstanding) { Enumeration e = _orders.elements(); while (e.hasMoreElements()) { Order each = (Order) e.nextElement(); outstanding += each.getAmount(); return outstanding; private void printDetails(double outstanding) { ... } private void printBanner() { ... } Remove Assignments to Parameters refactoring
16
example (4c) void printOwing() { double outstanding = 0.0; printBanner(); outstanding = getOutstanding(outstanding); printDetails(outstanding); } private double getOutstanding(double initialValue) { double result = initialValue; Enumeration e = _orders.elements(); while (e.hasMoreElements()) { Order each = (Order) e.nextElement(); result += each.getAmount(); return result; private void printDetails(double outstanding) { ... } private void printBanner() { ... } Rename In this case the outstanding variable is initialized only to an obvious initial value We can initialize it only within the extract method.
17
variant: computed previous value
void printOwing(double previousAmount) { Enumeration e = _orders.elements(); double outstanding = previousAmount * 1.2; printBanner(); // calculate outstanding while (e.hasMoreElements()) { Order each = (Order) e.nextElement(); outstanding += each.getAmount(); } printDetails(outstanding); private void printDetails(double outstanding) { ... } private void printBanner() { ... }
18
variant, refactored void printOwing(double previousAmount) {
double outstanding = previousAmount * 1.2; printBanner(); outstanding = getOutstanding(outstanding); printDetails(outstanding); } private double getOutstanding(double initialValue) { double result = initialValue; Enumeration e = _orders.elements(); while (e.hasMoreElements()) { Order each = (Order) e.nextElement(); result += each.getAmount(); return result; private void printDetails(double outstanding) { ... } private void printBanner() { ... }
19
Extract Method: Considerations
if the extracted code changes multiple local variables: pick different code to extract aim for multiple methods, each with one return value try applying Replace Temp With Query in essence, recomputing the value each time not always (directly) possible performance issues ...or bring in the heavy machinery: Replace Method with Method Object turns the method into its own object
20
Problem case void printOwing() { Enumeration e = _orders.elements();
double outstanding = 0.0; int count = 0; printBanner(); // calculate outstanding while (e.hasMoreElements()) { Order each = (Order) e.nextElement(); outstanding += each.getAmount(); count = count + 1; } printDetails(outstanding, count); private void printDetails(double outstanding, int count) { ... } private void printBanner() { ... }
21
Inverse operation of Extract Method: Inline method
replace a method call with the body of the called method commonly applied by optimizing compilers this is the inverse operation of Extract Method when to apply: as an intermediate step when a group of methods is badly factored when too much delegation makes code hard to understand various complicating issues: polymorphic call sites recursion multiple return points
22
Inline Temp you want to eliminate a temporary variable that is used to hold the result of a method call mechanics: declare temp as final & compile compiler will alert you if there are multiple assignments replace all references to the temp with calls to that method. complicating issues: Method call may have side-effects try applying Separate Query from Modifier Method call may have a different value at the places where the temp is used temps that are assigned more than once (in loops) performance issues
23
Example: Inline Temp boolean checkBasePrice(){
int basePrice = anOrder.basePrice(); return (basePrice > 1000); } boolean checkBasePrice(){ final int basePrice = anOrder.basePrice(); return (basePrice > 1000); } boolean checkBasePrice(){ return (anOrder.basePrice() > 1000); } … (anOrder.basePrice() > 1000);
24
Replace Temp with Query
you want to eliminate a temporary variable that is used to hold the result of an expression mechanics: declare temp as final & compile compiler will alert you if there are multiple assignments extract the expression into a method. replace all references to the temp with calls to that method. complicating issues: expression may have side-effects try applying Separate Query from Modifier expression may have a different value at the places where the temp is used temps that are assigned more than once (in loops) performance issues
25
Example: Replace Temp with Query
double getPrice(){ int basePrice = _quantity * _itemPrice; double discountFactor; if (basePrice > 1000) discountFactor = 0.95; else discountFactor = 0.98; return basePrice * discountFactor; }
26
Extract the expression
Example (2) double getPrice() { final int basePrice = _quantity * _itemPrice; double discountFactor; if (basePrice > 1000) discountFactor = 0.95; else discountFactor = 0.98; return basePrice * discountFactor; } private int basePrice() { return _quantity * _itemPrice; Declare as final Extract the expression
27
Replace reference to temp
Example (2) double getPrice() { final int basePrice = basePrice(); double discountFactor; if (basePrice > 1000) discountFactor = 0.95; else discountFactor = 0.98; return basePrice * discountFactor; } private int basePrice() { return _quantity * _itemPrice; Replace reference to temp
28
Example (3) Replace all references Replace all references
double getPrice() { final int basePrice = basePrice(); double discountFactor; if (basePrice() > 1000) discountFactor = 0.95; else discountFactor = 0.98; return basePrice() * discountFactor; } public int basePrice() { return _quantity * _itemPrice; Replace all references Replace all references
29
Example (3) double getPrice() { final int basePrice = basePrice();
double discountFactor; if (basePrice() > 1000) discountFactor = 0.95; else discountFactor = 0.98; return basePrice() * discountFactor; } public int basePrice() { return _quantity * _itemPrice; Remove temp Declaration Repeat the process for discountFactor
30
Example (4) double getPrice() { return basePrice() * discountFactor();
} private double discountFactor() { double discountFactor; if (basePrice() > 1000) discountFactor = 0.95; else discountFactor = 0.98; return discountFactor; private int basePrice() { return _quantity * _itemPrice;
31
Example (5) double getPrice() { return basePrice() * discountFactor(); } private double discountFactor() { return (basePrice() > 1000) ? 0.95 : 0.98; private int basePrice() { return _quantity * _itemPrice;
32
Replace Temp with Query – compositional view
Consists of three primitive refactorings: Finalize temp. Extract method Inline temp. Replace Temp with Query: Finalize temp Finalize temp Extract method Extract method Inline temp Inline temp
33
Replace Method with Method Object
you have a long method that uses local variables in such a way that you cannot apply Extract Method Turn the method into its own object so that: local variables become fields you can then decompose the method into other methods on the same object
34
Replace Method with Method Object
35
Creating a Method Object: Mechanics
Create a new class, name it after the method. This class should contain: final field for the “source object” that hosted the original method field for each temporary variable and parameter in the method constructor that takes the source object and each parameter; initializes corresponding fields a new method called compute() with the same return type as the source method copy body of original method into compute() use source object field for any invocations of methods on the original object Compile. Replace the old method with one that creates the new object and calls compute.
36
Example public class Account { int gamma(int inputVal,int quantity, int yearToDate){ int importantValue1 = (inputVal*quantity) +delta(); int importantValue2 = (inputVal*yearToDate) + 100; if ((yearToDate - importantValue1) > 100){ importantValue2 -= 20; importantValue1 += 20; } int importantValue3 = importantValue2 * 7; return importantValue3 - 2*importantValue1;
37
Step 1: create a new class
named after the original method field for “source object” of type Account field for each parameter/temp class Gamma { private final Account _account; private int inputVal; private int quantity; private int yearToDate; private int importantValue1; private int importantValue2; private int importantValue3; }
38
Step 2: add constructor & compute()
class Gamma { public Gamma(Account source, int inputValArg, int quantityArg, int yearToDateArg){ _account = source; inputVal = inputValArg; quantity = quantityArg; yearToDate = yearToDateArg; } public int compute() { ... } private final Account _account; private int inputVal; private int quantity; private int yearToDate; private int importantValue1; private int importantValue2; private int importantValue3;
39
Step 3: move code into compute() & update source method
public class Account { int gamma(int inputVal, int quantity, int yearToDate){ return new Gamma(this, inputVal, quantity, yearToDate).compute(); } class Gamma { public Gamma(Account source, int inputValArg, int quantityArg, int yearToDateArg){...} public int compute() { importantValue1 = (inputVal*quantity) + _account.delta(); importantValue2 = (inputVal*yearToDate) + 100; if ((yearToDate - importantValue1) > 100) {importantValue2 -= 20; importantValue1 += 20;} int importantValue3 = importantValue2 * 7; return importantValue3 - 2*importantValue1; ...
40
Decompose source method: Apply Extract Method
class Gamma { public Gamma(Account source, int inputValArg, int quantityArg, int yearToDateArg){...} public int compute() { importantValue1 = (inputVal*quantity) + _account.delta(); importantValue2 = (inputVal*yearToDate) + 100; importantThing(); int importantValue3 = importantValue2 * 7; return importantValue3 - 2*importantValue1; } private void importantThing(){ if ((yearToDate - importantValue1) > 100) {importantValue2 -= 20; importantValue1 += 20;} ...
41
Move Method A method is using or is used by more features of another class than the class on which it is defined. Create a new method with a similar body in the class it uses most. either turn the old method into a simple delegation, or remove it altogether. Class1 Class2 Class1 Class2
42
Move Method - thoughts Moving methods is the bread and butter of refactoring. Move methods when classes have too much behavior classes are collaborating too much and are too highly coupled. Once I see a likely method to move, I take a look at the methods that call it, the methods it calls, and any redefining methods in the hierarchy. I assess whether to go ahead on the basis of the object with which the method seems to have more interaction. It's not always an easy decision to make. If I am not sure whether to move a method, I go on to look at other methods. .. Sometimes the decision still is hard to make. .. If it is difficult to make the decision, it probably does not matter that much. Then I choose according to instinct; after all, I can always change it again later.
43
Move Method: Mechanics
Examine features used by the source method that are defined on the source class. Consider whether they should be moved as well. check for overriding definitions in subclasses/superclasses in general, cannot move if there is polymorphism declare method in the target class ( choose different name?) copy code from source method adjust references to source determine how to access target object from source turn source method into delegating method removal is optional Compile target class If a feature is used only by the method you are about to move, you might as well move it, too. If the feature is used by other methods, consider moving them as well. Sometimes it is easier to move a clutch of methods than to move them one at a time. Determine how to reference the correct target object from the source. There may be an existing field or method that will give you the target. If not, see whether you can easily create a method that will do so. Failing that, you need to create a new field in the source that can store the target. This may be a permanent change, but you can also make it temporarily until you have refactored enough to remove it. Turn the source method into a delegating method. Compile and Test
44
Move Method: Example move this method into class AccountType
public class Account { double overdraftCharge(){ if (_type.isPremium()){ double result = 10; if (_daysOverdrawn > 7) result += (_daysOverdrawn - 7)*0.85; return result; } else return _daysOverdrawn*1.75; } double bankCharge(){ double result = 4.5; if (_daysOverdrawn > 0) result += overdraftCharge(); private AccountType _type; private int _daysOverdrawn; move this method into class AccountType …there are going to be several new account types, each of which has its own rule for calculating the overdraft charge
45
Example observations reference to field _daysOverdrawn in source class; we assume that this field needs to stay in source class source class already has pointer to target class options for making a feature available to the moved method in its new position: move it to the target class as well create or use a reference to the source class from the target class pass the whole source object as a parameter needed if moved method calls methods on source object if the feature is a field, pass it as a parameter
46
Move Method: Example (2)
remove the _type from uses of features of the account type class AccountType { boolean isPremium(){ ... } } double overdraftCharge(int daysOverdrawn) { if (isPremium()) { double result = 10; if (daysOverdrawn > 7) result += (daysOverdrawn - 7) * 0.85; return result; } else return daysOverdrawn * 1.75; } Option 4 copy the method body over to the account type and get it to fit.
47
Move Method: Example (3)
replace source method body with delegation the code now compiles and can be used as-is public class Account { double overdraftCharge() { return _type.overdraftCharge(_daysOverdrawn); } double bankCharge() { double result = 4.5; if (_daysOverdrawn > 0) result += overdraftCharge(); return result; private AccountType _type; private int _daysOverdrawn;
48
Move Method: Example (4)
removing the source method requires updating all references to it (apply Inline Method) public class Account { double bankCharge() { double result = 4.5; if (_daysOverdrawn > 0) result += _type.overdraftCharge(_daysOverdrawn); return result; } private AccountType _type; private int _daysOverdrawn; class AccountType { ... }
49
Move Method: Related Refactorings
Move Field move a field from source class to target class similar issues Extract Class break up a single class into two classes create a new class that will contain some of the functionality of the source class create link between source and target class (e.g., in constructor of source class) move functionality to target class with repeated applications of Move Method and Move Field
50
Refactorings for Eliminating Type Codes
used for restructuring “procedural” programs that use type codes Replace Type Code with Class for modeling enumerated types assumes no conditional logic involving type codes Replace Type Code with Subclasses conditional logic involving type codes assumes type code is frozen at object creation time enables use of Replace Conditional with Polymorphism to replace conditional logic with dynamic dispatch Replace Type Code with State/Strategy can accommodate more dynamic situations where the type code changes at run-time
51
Replace TC with Class Symbolic name is only an alias
The compiler still sees the underlying number. The compiler type checks using the number not the symbolic name. Any method that takes the type code as an argument expects a number, and there is nothing to force a symbolic name to be used. This can reduce readability and be a source of bugs.
52
Replace TC with Class: Mechanics
create a new class for the type code with code field that matches type code static variables for allowable instances static method for retrieving appropriate instance given type code modify implementation of source class to use the constants defined in new class maintain old type-code based interface update methods on source class that use the type code so they use the new class instead apply Rename Method to update names of accessor methods add accessor method that uses new class add constructor that uses new class update clients of source class, one by one after updating all clients, remove old interface
53
Replace TC with Class: Example
public class Person { public static final int O = 0; public static final int A = 1; public static final int B = 2; public static final int AB = 3; private int _bloodGroup; public Person(int bloodGroup){ _bloodGroup = bloodGroup; } public void setBloodGroup(int arg){ _bloodGroup = arg; { public int getBloodGroup(){ return _bloodGroup;
54
Step 1: create new class BloodGroup
public static final BloodGroup O = new BloodGroup(0); public static final BloodGroup A = new BloodGroup(1); public static final BloodGroup B = new BloodGroup(2); public static final BloodGroup AB = new BloodGroup(3); private static final BloodGroup[] _values ={O,A,B,AB}; private final int _code; private BloodGroup (int code){ _code = code; } public int getCode(){ return _code; public static BloodGroup code(int arg){ return _values[arg]; {
55
Step 2: update code in Person to use constants in class BloodGroup
public class Person { public static final int O = BloodGroup.O.getCode(); public static final int A = BloodGroup.A.getCode(); public static final int B = BloodGroup.B.getCode(); public static final int AB = BloodGroup.AB.getCode(); private BloodGroup _bloodGroup; public Person(int bloodGroup){ _ bloodGroup = BloodGroup.code(bloodGroup); } public void setBloodGroup(int arg){ _ bloodGroup = BloodGroup.code(arg); { public int getBloodGroup(){ return _bloodGroup.getCode();
56
Step 3: rename old accessor methods; add new constructor & accessors
public class Person { ... // old constructor public Person(int bloodGroup){ _bloodGroup = BloodGroup.code(bloodGroup); } // old accessor (renamed) public void setBloodGroupCode(int arg){ _bloodGroup = BloodGroup.code(arg); // new constructor public Person(BloodGroup bloodGroup){ _bloodGroup = bloodGroup; // new accessor public void setBloodGroup(BloodGroup arg){ _bloodGroup = arg; …
57
Step 5: after updating all clients (step4) , remove all type-code based interface
public class Person { private BloodGroup _bloodGroup; public Person(BloodGroup bloodGroup){ _bloodGroup = bloodGroup; } public void setBloodGroup(BloodGroup arg){ _bloodGroup = arg; { class BloodGroup { ... }
58
Replace TC with Subclasses
If the type code affects behavior-use polymorphism to handle the variant behavior. This situation usually is indicated by the presence of case-like conditional statements.
59
Replace TC with Subclasses
apply when you have an immutable type code with conditional control flow based on its value mechanics: preparation: redirect access to type-code field via getter/setter method (see: Self- Encapsulate Field) if a constructor uses type code as a parameter, replace it with a factory method (see Replace Constructor with Factory Method) for each value of the type code, create new subclass override getting method in each subclass to return the relevant value
60
Replace TC with Subclasses: Example
public class Employee { private int _type; static final int ENGINEER = 0; static final int SALESMAN = 1; static final int MANAGER = 2; Employee(int type){ _type = type; }
61
step 1: preparation public class Employee { private int _type; static final int ENGINEER = 0; static final int SALESMAN = 1; static final int MANAGER = 2; // add getter method int getType(){ return _type; } // factory method to replace constructor static Employee create(int type){ return new Employee(type); } // constructor now made private private Employee(int type){_type = type;} {
62
step 2: introduce subclasses
public abstract class Employee { static final int ENGINEER = 0; static final int SALESMAN = 1; static final int MANAGER = 2; abstract int getType(); static Employee create(int type){ switch(type){ case ENGINEER: return new Engineer(); case SALESMAN: return new Salesman(); case MANAGER: return new Manager(); default: throw new IllegalArgumentException( "incorrect type value"); } { class Engineer extends Employee { int getType(){ return Employee.ENGINEER; } class Salesman extends Employee { ... } class Manager extends Employee { ... }
63
Replace TC with State/Strategy (1)
This is similar to Replace Type Code with Subclasses used if: the type code changes during the life of the object another reason prevents subclassing It uses either the state or strategy pattern
64
Replace TC with State/Strategy (3)
apply when you have an mutable type code with conditional control flow based on its value mechanics: redirect access to type-code field via getter/setter method (see: Self-Encapsulate Field) create State class, name it after purpose of type code with abstract query to return the type code add a subclass of the State for each type code override query method of State class
65
Replace TC with State/Strategy (2)
Mechanics (continued): Add type code information to State class Static variables for code values. Create a factory method for State subclasses. Change type query methods (getTypeCode) in State subclasses. Update clients of source class, one by one. update source class create field to refer to State object update TC query method to delegate to State object update TC setting method to change State
66
Replace TC with State/Strategy: Example
public class Employee { private int _type; static final int ENGINEER = 0; static final int SALESMAN = 1; static final int MANAGER = 2; Employee(int type){ _type = type; } int payAmount(){ switch (_type){ case ENGINEER: return _monthlySalary; case SALESMAN: return _monthlySalary+_commission; case MANAGER: return _monthlySalary + _bonus; default: throw new RuntimeException( "Incorrect employee"); { }
67
Step 1: Self-encapsulate field
public class Employee { private int _type; static final int ENGINEER = 0; static final int SALESMAN = 1; static final int MANAGER = 2; Employee(int type){setType(type); } int getType(){ return _type; } void setType(int type){ _type = type; } int payAmount(){ switch (getType()){ case ENGINEER: return _monthlySalary; ... {
68
Step 2: Declare State Class, subclasses, and query methods
abstract class EmployeeType { abstract int getTypeCode(); { class Engineer extends EmployeeType { int getTypeCode(){ return Employee.ENGINEER; } class Manager extends EmployeeType { int getTypeCode(){ return Employee.MANAGER; } class Salesman extends EmployeeType { int getTypeCode(){ return Employee.SALESMAN; }
69
Step 3: Copy type definitions to State class; introduce factory method
abstract class EmployeeType { // add factory method static EmployeeType newType(int code){ switch (code){ case ENGINEER: return new Engineer(); case SALESMAN: return new Salesman(); case MANAGER: return new Manager(); default: { throw new IllegalArgumentException("bad code"); } { static final int ENGINEER = 0; static final int SALESMAN = 1; static final int MANAGER = 2; abstract int getTypeCode(); } class Engineer extends EmployeeType { int getTypeCode(){ return ENGINEER; }
70
Step 4: update source class (Employee)
public class Employee { private EmployeeType _type; int getType(){ return _type.getTypeCode(); } void setType(int arg){ // change state _type = EmployeeType.newType(arg); int payAmount(){ switch (getType()){ // update references to constants case EmployeeType.ENGINEER: return _monthlySalary; case EmployeeType.SALESMAN: return _monthlySalary + _commission; case EmployeeType.MANAGER: return _monthlySalary + _bonus; default: throw new RuntimeException( "Incorrect employee"); {
71
Replace Conditional with Polymorphism
72
Replace Conditional with Polymorphism
often used in combination with Replace TC with Subclasses and Replace TC with State/Strategy moves each “leg” of conditional to an overriding method in a subclass mechanics if necessary, use Move Method to place the conditional in a method at the top of the inheritance structure may require adding get/set methods for fields of source class accessed in conditional pick a subclass, create overriding definition and move appropriate leg of conditional into it may require making private members of superclass protected
73
Step 1: Move payAmount() to EmployeeType
abstract class EmployeeType { ... // moved to EmployeeType 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"); {
74
Step 2:Delegate payAmount() to EmployeeType
public class Employee { private EmployeeType _type; int getType(){ return _type.getTypeCode(); } void setType(int arg){ // change state _type = EmployeeType.newType(arg); { // now delegates to EmployeeType int payAmount(){ return _type.payAmount(this); } private int _monthlySalary; private int _commission; private int _bonus; // accessors added int getMonthlySalary(){ return _monthlySalary; } int getCommission(){ return _commission; } int getBonus(){ return _bonus; }
75
Step 3: Now start overriding payAmount()
abstract class EmployeeType { ... int payAmount(Employee emp){ switch (getTypeCode()){ case ENGINEER: throw new RuntimeException("should be overridden"); case SALESMAN: return emp.getMonthlySalary() + emp.getCommission(); case MANAGER: return emp.getMonthlySalary() + emp.getBonus(); default: throw new RuntimeException("Incorrect employee"); { class Engineer extends EmployeeType { return emp.getMonthlySalary(); }
76
Step 4: Final situation, after removing body of method in State class
abstract class EmployeeType { abstract int payAmount(Employee emp); } class Engineer extends EmployeeType { int payAmount(Employee emp){ return emp.getMonthlySalary(); { class Manager extends EmployeeType { return emp.getMonthlySalary() + emp.getCommission(); class Salesman extends EmployeeType { return emp.getMonthlySalary() + emp.getBonus();
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.