Presentation is loading. Please wait.

Presentation is loading. Please wait.

Refactoring Refactoring plays a major role in the decisions of where to put responsibilities. While deciding where these responsibilities lie, a refactoring.

Similar presentations


Presentation on theme: "Refactoring Refactoring plays a major role in the decisions of where to put responsibilities. While deciding where these responsibilities lie, a refactoring."— Presentation transcript:

1 Refactoring Refactoring plays a major role in the decisions of where to put responsibilities. While deciding where these responsibilities lie, a refactoring called Move Method and Move Field is valuable. If classes get too large with too many responsibilities, consider using Extract Class. On the other hand if classes get too small with few responsibilities consider Inline Class. Moving Features between Objects

2 Refactoring If another class is being used, it may be necessary to hide the fact that the class is needed by Hide Delegate. If too many hidden methods exist in one class, consider Remove Middle Man. If you are not able to access code to add new methods, use Introduce Foreign Method to add the functionality into a foreign class or Introduce Local Extension to extend the functionality of the non-modifiable class. Moving Features between Objects

3 Refactoring 1. Move Method 2. Move Field 3. Extract Class – already covered in class lesson 4. Inline Class – already covered in class lesson 5. Hide Delegate 6. Remove Middle Man 7. Introduce Foreign Method 8. Introduce Local Extension Moving Features between Objects

4 Refactoring Summary: You have a method that is used by more features of another class than its own class. Move the method to the class that better serves the responsibility of the method. 1. Move Method

5 Refactoring 1. Move Method: Motivation: Methods may be moved when you discover that a class has too much responsibility or too many methods. You may also discover that the method is coupled with too many other classes and/or collaborating too much with other classes.

6 Refactoring 1. Move Method: Example:version 0 Class Account… double overdraftCharge() { if ( _type.isPremium()) { double result = 10; if (_dayOverdrawn > 7) result += (_daysOverdrawn – 7) * 0.85; return result; }// end if else return _daysOverdrawn * 1.75; } // end overdraftChrge double bankCharge () { double result = 4.5; if (_daysOverdrawn > 0) result += overdraftCharge(); return result; } // end bankCharge private AccountType _type; private int _daysOverdrawn; Suppose you wish to have other account types and decide to build an account type class to contain the methods

7 Refactoring 1. Move Method: Example: version 1 Class AccountType…several account types – diff method double overdraftCharge(int daysOverdrawn) { if (isPremium()) { double result = 10; if (dayOverdrawn > 7) result += (daysOverdrawn – 7) * 0.85; return result; }// end if else return _daysOverdrawn * 1.75; } // end overdraftChrge Class Account… double bankCharge () { double result = 4.5; if (_days|Overdrawn |> 0) result += _type.overdraftCharge|(daysOverdrawn); return result; } // end bankCharge Overdraft….copied to accounttype days overdrawn param to method Replace method with simple delegation I have now extracted method and moved it to another class

8 Refactoring 1. Move Method: Mechanics: examine method to assure what should be moved check sub and superclasses as to method polymorphism declare method in target class – copy code adjust method to make it fit (see note next page) if many references build delegate method in source write call to new method in source compile and test

9 Refactoring 1. Move Method: Mechanics: if you need features of the source class when you move the method you can: 1) move the feature to the target 2) create or use a reference to the feature 3) pass the source object as parameter 4) if variable, pass the variable

10 Refactoring Summary: You have a field that is used by another class more than the class in which it resides. Move the field to the class which uses the field the most. 2. Move Field

11 Refactoring 2. Move Field: Motivation: The get and set method encapsulate a field and should be used at all time, even within the class itself. If these get and set methods, therefore the field itself is used more by other classes, consider moving the field If you want to extract class, the fields are the first thing to go in the new class.

12 Refactoring 2. Move Field: Example: Class Account… private AccoutnType _type; private double _interestRate; double interestForAmount)days (double amount, int days) { return )interestRate * amount * days /365; } // end interestForAmount Would like to move interestRate to the AccountType class because it is used more in that class.

13 Refactoring 2. Move Field: Example: Class AccountType…. private double _interestRate; void setInterestRate (double arg) { _interestRate = arg; } double getInterestRate () { return _interestRate; } ………. double interestForAccount_days (double amount, int days) { return _type.getInterestRate() * amount * days/365; } // end interestForAccount_days Move _interestRate. reference get and set methods for the field. In the original class

14 Refactoring 2. Move Field: Mechanics: if the field is public Encapsulate Field compile and test create field in target class with get and set compile target define references to get and set remove field from source

15 Refactoring Summary: A client is calling a delegate class of an object Create methods on the server to hide the delegate. 5. Hide Delegate

16 Refactoring 5. Hide Delegate: Motivation: Encapsulation is the key to minimizing the impact of maintenance changes.

17 Refactoring 5. Hide Delegate: Client Class Person getDept() Department getManager() Client Class Person getManager() Department Client must know about Person and Department Person must know about department Client must know about Person Person must know about department

18 Refactoring 5. Hide Delegate: Example: p 157 Class Person { Department _department; public Department getDepartment() { return _department;} public void setDepartment (Department arg) { _department = arg;} } // end person Class Department { private String _chargeCode; private Person _manager; public Department (Person manager) { _manager = manager; } public Person getManager() { return _manager; } manager = john.getDepartment().getManager(); If a client wants to know a manager, it needs to get the department first.

19 Refactoring 5. Hide Delegate: Example: public Person getManager () { return _department.getManager(); } // end getManager manager = jphn.getManager(); Create a delegate method to hide the department class from the client. Rewrite reference to use delegate method.

20 Refactoring 5. Hide Delegate: Mechanics: create needed delegate methods adjust the client to call the server compile and test

21 Refactoring Summary: A class is doing too much simple delegation. Get the client to call the delegate directly. 6. Remove Middle Man

22 Refactoring 6. Remove Middle Man: Motivation: When the server becomes just a middle man for all the delegate methods, you need to change the client to call the delegate directly. You have to evaluate and decide how much to hide and delegate.

23 Refactoring 6. Remove Middle Man: Client Class Person getDept() Department getManager() Client Class Person getManager() Department Client must know about Person and Department Person must know about department Client must know about Person Person must know about department

24 Refactoring 6. Remove Middle Man: Example: class Person…. Department _department; public Person getManager () { return _department.getManager*(; class Department… private Person _manager; public Department (Person manager) { _manager = manager; } manager = john.getManager(); Person is hiding the department.

25 Refactoring 6. RemoveMiddle Man: Example: class Person…. public Department getDepartment() { return _department; } Department _department; public Person getManager () { return _department.getManager*(; manager = john.getDepartment.getManager(); Now department is exposed.

26 Refactoring 6. Remove Middle Man: Mechanics: create an accessor for the delegate for each client using the delegate remove the method from the server replace the call with a method compile and test

27 Refactoring Summary: A server class needs an additional method but you cannot modify the class. Create a method in the client with an instance of the server class as its first argument. 7. Introduce Foreign Method

28 Refactoring 7. Introduce Foreign Method: Motivation: You have a class that needs to have a method added but you cannot (for whatever reason) modify the class and add the needed method. You could just write the method but if it needs reusing you do not want to repeat the code several times (a cardinal sin). Therefore create a foreign method. A foreign method is a method foreign to its own class but needs to be there since its class cannot be modified.

29 Refactoring 7. Introduce Foreign Method: Example: Date newStart = new Date (previousEnd.getYear(), previousEnd.getMonth(), previousEnd.getDate()+ 1); Date newStart = nextDat(previousEnd); private static Date nextDay (Date arg) { return new Date (arg.getYear().arg.getMonth().arg.getDate() + 1); } // end nextDay becomes

30 Refactoring 7. Introduce Foreign Method: Mechanics: create a method in the client make an instance of the server class as the first parameter comment the method as foreign method

31 Refactoring Summary: A server class needs several additional methods but you cannot modify the class. Create a new class that contains these extra methods. Make this extension class a subclass or a wrapper of the original. 8. Introduce Local Extension

32 Refactoring 8. Introduce Local Extension: Motivation: When new methods are needed for an existing class and you cannot (for whatever reason) add these methods,and indeed there are several methods, you need to to extend the class and provide those methods in the new class.

33 Refactoring 8. Introduce Local Extension: Example: SUBCLASS Class mfDate extends Date { public nextDay() …… public dayOfYear()…… WRAPPER class mfDate { private Date _original; Need to decide if you wish to use a subclass (inheritance) or a wrapper (delegation)

34 Refactoring 8. Introduce Local Extension: Example: SUBCLASS Class MfDateSub extends Date { public mfDateSub (String dateString) { super (dateString); } Declare the sub class.

35 Refactoring 8. Introduce Local Extension: Example: SUBCLASS -- using the client class Class Client….. private static Date nextDay(Date arg) { // foreign method, should be on date return new Date (arg.getYear().arg.getMonth(), arg.getDate() + 1); } // end nextDay Class MfDate… Date nextDay() { return new Date (getYear().getMonth().getDate() +1); } Becomes.

36 Refactoring 8. Introduce Local Extension: Example: WRAPPER class mfDate { private Date _original;} // end mfDate public MfDateWrap (String dateString) { _original = new Date(dateString); } public MfDateWrap (Date arg) { _original = arg; } Declare the wrapping class. Original constructor with simple delegation. New constructor just sets the instance variable.

37 Refactoring 8. Introduce Local Extension: Example: WRAPPER public int getYear() { return )original.getYear(); } public boolean equals (myDateWrap arg) { return (toDate().equals(arg.toDate())); } Delegate all methods of original class to the new class – a few here

38 Refactoring 8. Introduce Local Extension: Example: WRAPPER class Client….. private static Date nextDay (Date arg) { // foreign method, should be on date return new Date (arg.getYear().arg.getMonth().arg.getDate() + 1); } // end nextDay Class MfDate…. Date nextDay() { return new Date (getYear().getMonth().getDate() + 1); } Now I can Move Method for behavior to place the method in the new class. So, this method becomes the method below.

39 Refactoring 8. Introduce Local Extension: Example: WRAPPER aWrapper.after(aDate)// can be made to work aWrapper.after(anotherWrapper)// can be made to work aDate.after(aWrapper)// will not work Since I cannot alter the original. Problem – using wrappers that take an original as an argument causes a particular problem since I cannot alter the original.

40 Refactoring 8. Introduce Local Extension: Example: WRAPPER public boolean equals (Date arg) // causes problems public boolean equalsDate (Date arg) Problem – using wrappers that use system methods does not hide the original. You would like to override equals like this. I can make it work with my situation but the rest of the software system depends symmetric behavior of equals. I need another method shown below.

41 Refactoring 8. Introduce Local Extension: Mechanics: create an extension class either as a subclass or wrapper add new features to the extension replace the original with the extension compile and test.

42 Refactoring Sometimes we need to make other things classes such as parameters, attributes or methods simply because of complexity and a need to pass as parameters. Building Classes

43 Refactoring Summary: You have a long method that uses local variables such than you cannot extract the method. Turn the method into its own object allowing further decomposition of the code in the method. Replace Method with Method Object

44 Refactoring Replace Method with Method Object: Motivation: Remember the goal is small methods, extracting pieces out of large methods allows you to make things less complicated and more comprehensible.

45 Refactoring Replace Method with Method Object: Example: 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; int importantValue3 = importantValue2 * 7; // and so on return importantValue3 – 2 * importantValue1; } // end gamma method Not a good method. But shows the approach.

46 Refactoring Replace Method with Method Object: Example: Class Gamma private final Account _account; private int inputVal; private int quantity; private int yearToDate; private int importantValue1; private int importantValue2; private int importantValue3; Build a class so you can use an object of this class. 1.Declare new class Gamma. 2.Provide a final field for original object - Account. 3.Provide fields for each param and temp variables.

47 Refactoring Replace Method with Method Object: Example: Gamma (Account source, int inputValArg, int quantityArt, int yearToDateArg) { _account = source; inputVal = inputValArg; quantity = quantityArg; yearToDate = yearToDateArg; } // end constructor Int compute () { int importantValue1 – (inputVal (quantity + _ account.delta(); int importantValue2 = (inputVal * yearToDate) + 100; if ( ( yearToDate – importantValue1) > 100) importantValue2 -=20; int importantValue3 – importantValue2 * 7; // and so on with an importantMethod (); return importantValue3 – 2 * importantValue1; } // end compute Build a constructor that uses the params. Transfer the method.

48 Refactoring Replace Method with Method Object: Example: int gamma (int inputVal, int quantity, int yearToDate) { return new Gamma (this, inputVal, quantity, yearToDate).compute (); }// end gamma void importantThing() { if (yearToDate – importantValue1) > 100) importantValue2 -=20; } // end importantThing Modify the old method to delegate it to the object. Now, EXTRACT any important METHODS.

49 Refactoring Replace Method with Method Object: Mechanics: create a new class create a constructor place the method in the new class examine method to EXTRACT METHODS replace the old method with a call to new class and methdod

50 Refactoring Summary: You have a data item that needs additional data or behavior. Turn the data item into an object. Replace Data Value with Object

51 Refactoring Replace Data Value with Object: Motivation: When you have many methods dealing with one attribute in a class, consider replacing that data value in your code with an object. To do this you will need to create a class for the attribute and extract the attribute to the class and extract the needed methods.

52 Refactoring Replace Data Value with Object: Example: class Order… public Order (String customer) { _customer = customer; } public String getCustomer () { return )customer; } public void setCustomer (String arg) { _customer = arg; } private String _customer; private static int numberOfOrdersFor (Collection orders, String customer) { int result = 0; Iterator iter = orders.iterator(); while (iter.hasNext() { Order each = (Order) iter.next(); if (each.getCustomerName().equals(customer)) result++; } // end while return result; } // end numberOfOrdersFor Using the new get and set methods.

53 Refactoring Summary: You need to interface with a record structure in a traditional programming environment. Make a dumb data object for the record. Replace Record with Data Class

54 Refactoring Replace Record with Data Class: Motivation: You have designed your database and you need a record structure, represented as a class, to attach the behavior for the attributes in the record structure. You define a data class to hold the format of the data.

55 Refactoring Replace Record with Data Class: Example: class Person { String ssn; String firstName; String lastName; String middleName; } // end Person Person

56 Refactoring Mechanics: create a class to represent record give the class private fields code get and set methods for each data item compile and test. Replace Record with Data Class:

57 Refactoring Summary: A class has a numeric type code that does not affect its behavior. Replace the number with the new class. Replace Type Code with Class

58 Refactoring Replace Type Code with Class: Motivation: Numeric Type codes, are used to identify types of data and often to invoke particular behavior.

59 Refactoring Replace Type Code with Class: Example: 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 = arg;} public void setBloodGroup (int arg) { _bloodGroup = arg; } public int getBloodGroup () { return _bloodGroup; } A person has a blood group modeled with a type code

60 Refactoring Replace Type Code with Class: Example: 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 = (0,A,B,AB); private BloodGroup (int code) {_code = code;} public int getCode() { return _code; } public static BloodGroup code (int arg) {return _values[arg]; } }// end BloodGroup Create new blood group class with instances containing type code.

61 Refactoring Replace Type Code with Class: Example: class Person{ 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 = (0,A,B,AB); private BloodGroup _bloodGroup; public Person (int BloodGroup) { _bloodGroup = Bloodgroup.code(bloodGroup);} public int getBloodGroup() { return _bloodGroup.getCode(); } public void setBloodGroup (int arg) { _bloodGroup = BloodGroup.code (arg); } } // end Person Replace code in Person with code using new class.

62 Refactoring Replace Type Code with Class: Example: class Person …. public int getBloodGroupCode () { return _bloodGroup.getCode(); } public BloodGroup getBloodGroup() { return )bloodGroup; } public Person (BloodGroup bloodGroup) { _bloodGroup = bloodGroup;} public void setBloodGroup(BloodGroup arg) { _bloodGroup = arg; } Rename the method on the accessor Add a getting method Create a new constructor and setting method. I

63 Refactoring Replace Type Code with Class: Example: Person thePerson = new Person(Person.A); Person thePerson – new Person (BloodGroup.A); thePerson.getBloodGroupCode() thePerson.getBloodGroup().getCode() Change static variable references becomes References to the getting method need to use new one. method. I becomes

64 Refactoring Replace Type Code with Class: Example: thePerson.setBloodGroup(Person.AB) thePerson.setBloodGroup(BloodGroup.AB) 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 = (0,A,B,AB); public Person (int BloodGroup) { _bloodGroup = Bloodgroup.code(bloodGroup);} public int getBloodGroup() { return _bloodGroup.getCode(); } public void setBloodGroup (int arg) { _bloodGroup = BloodGroup.code (arg); } References to the setting method also need new ones becomes Remove the getting, constructor, static defs and setting methods using integer I

65 Refactoring Replace Type Code with Class: Example: class BloodGroup{ //….. private int getCode() { return _code; } private static BloodGroup code (int arg) {return _values[arg]; } }// end BloodGroup Privatize methods on blood group that use the code I

66 Refactoring Replace Type Code with Class: Mechanics: create a new class for type code modify implementation of source for new class for each method create a new method using new class change clients of source to use new interface remove old interface compile and test.

67 Refactoring Replace Data Value with Object: Mechanics: create a class for the value add getter and setter change the type of the field to the new class change references to the new class add the constructor compile and test

68 Refactoring Summary: You have repeated checks for a null value. Replace the null value with a null object. Introduce Null Object

69 Refactoring Introduce Null Object: Motivation: In polymorphism, doing the right thing depends on type. One of the less intuitive places to do this is a place where you have null value objects.

70 Refactoring Introduce Null Object: if (customer == null) plan = BillingPlan.basic (); else plan = customer.getPlan(); Given GOES TO when you would have several other subtypes Customer ____________ getPlan Null Customer ____________ getPlan

71 Refactoring Introduce Null Object: Class Site…. Customer getCustomer () { return _customer; } Customer _customer; Class Customer… public String getName () { …} public BillingPlan getPlan () { …} public PaymentHistory getHistory () {…} Given Some features of customer

72 Refactoring Introduce Null Object: public class PaymentHistory…. int getWeeksDeliquentInLastYear() Customer customer = site.getCustomer(); // gets a customer at a site BillingPlan Plan; if (Customer == full_ plan = BillingPlan.basic(); else plan = customer.getPlan(); String customerName; if (customer == null) customerName = “occupant”; else customerName = customer.getName(); int weeksDelinquent; if (customer == null) weekDeliquent = 0; else weeksDelinquent _customer.getHistory().getWeeksDelinquentInLastYear(); Payment history has its own features Some features of customer

73 Refactoring Introduce Null Object: public class NullCustomer extends Customer { public boolean isNull () { return true; } } // end NullCustomer protected Customer () { } // needed for Null Customer interface Nullable { boolean isNull (); } class Customer implements Nullable Many clients have to check for null thus we need a null object. If you cannot modify Customer use a testing interface

74 Refactoring Introduce Null Object: class Customer ….. static Customer newNull() { return new NullCustomer(); } } // end NullCustomer class Site… Customer getCustomer () { return (_customer == null) ? Customer.newNull(); )customer; } // end getCustomer Add a factory method to create null customers Return this new null object whenever I expect a null

75 Refactoring Introduce Null Object: Customercustomer = site.getCustomer(); BillingPlan plan; if (customer.isNull()) plan = BillingPlan.basic(); else plan = customer.getPlan(); String customerName; if (customer.isNull() ) customerName = “occupant”; else customerName – customer.getName(); int weeksDelinquent; if (customer.isNull()) weeksDelinquent = 0; else weeksDeliquent = customer.getHistory().getWeeksDelinquentInLastYear(): Alter all uses of value so they test with isNull() rather than == null

76 Refactoring Introduce Null Object: String customerName; if (customer.isNull()) customerName = “occupant”; else customerName = customer.getName(); Class NullCustomer…. public String getName () { return “occupant”; } String customerName = customer.getName(); Find all places nullness is tested and replace them. Then, move behavior to the null and remove conditionals. Given ::: Add suitable name method to the null customer Eliminate the conditional code

77 Refactoring Introduce Null Object: Mechanics: Create a subclass of the source class to act as a null version of the class. Find all places you can give out a null when asked for a source object. Find all places that compare a variable of the source type with null and replace them with a call isNull. Look for cases in which clients invoke an operation if not null and do some alternative behavior if null. Override the operation in the null class with the alternative behavior. Remove the condition check for overriden behavior, compile, and test.

78 Refactoring Summary: You have an array in which certain elements mean different things. Replace the array with an object that has a field for each element. Replace Array with Object

79 Refactoring Replace Array with Object: Motivation: You have a collection of objects BUT those objects are not similar. Arrays should not be composed of objects not similar. Replace the dissimilar array with an object.

80 Refactoring Replace Array with Object: Example: String[] row = new String[3]; row [0] = “Liverpool”; row [1] = ’13”; String name = row(0); int wins = Integer.parseInt(row(1)); The original array. Code using the array.

81 Refactoring Replace Array with Object: Example: class Performance {} row._data[0] = “Liverpool”; row._data [1] = ’13”; String name = row._data(0); int wins = Integer.parseInt(row._data(1)); Create and access the array.

82 Refactoring Replace Array with Object: Example: class Performance {} public String getName() { return _data[0]; public void setName (String arg) {_data[0] = arg; } String name = row.getName(); int wins = Integer.parseInt(row._data[1]); private String [].data = new String[3]; Add getters and setters. Make the array private.

83 Refactoring Replace Array with Object: Example: class Performance {} public String getName() { return _name; public void setName (String arg) {_name = arg; } private String _name; Change the interface and replace array internally by adding a field for each array element and changing accessors.

84 Refactoring Replace Array with Object: Mechanics: create a new class for the array change all users of the array to use the class add getters and setters for each element for each element, create a field delete the array compile and test

85 Refactoring Determining parameters for methods should be guided by one underlying principle (minimal coupling). This includes both method and class coupling. Parameters

86 Refactoring Summary: You have two classes that need to use each other’s features, but there is only a one-way link. Add back pointers, and change modifiers to update both sets. Change Unidirectional Association to Bidirectional

87 Refactoring Change Unidirectional Association to Bidirectional: Motivation: you have a cluster of code which can be grouped into a method. turn the cluster in to a method with a name that explains the function of the method

88 Refactoring Change Unidirectional Association to Bidirectional: class Order… Customer getCustomer () { return _customer; } void setCustomer (Customer arg) { _customer = arg; } Customer _customer; class Customer { private Set _orders = new HashSet(); A simple program has an order referring to a customer Customer has no reference to order

89 Refactoring Change Unidirectional Association to Bidirectional: class Customer… set friendOrders(); { // should only be used by order when modifying assoociation return orders; } // end friendOrders class Order… void setCustomer (Customer arg)… if ()customer is null) _customer.friendOrders*(.remove(this); _customer = arg; if (_customer is null) _customer.friendOrders().add(this); } // end setCustomer Allow order to take charge & add helper method to customer Update the modifier to update back pointers

90 Refactoring Change Unidirectional Association to Bidirectional: class Customer… void addOrder (Order arg) { arg.setCustomer(this); } class Order… // controlling methods void addCustomer (Customer arg) arg.friendOrders().add(thid); _customers.add(arg); } // end addcustomer void removeCustomer (Customer arg) { Let customer call the controlling method. With a m..m relationship, the methods look like:

91 Refactoring Change Unidirectional Association to Bidirectional: void removeCustomer (Customer arg) arg.friendOrders().remove(this); _customers.remove(arg); } // end removecustomer class Customer.. void addOrder(Order arg) { arg.addCustomer(this); } void removeOrder (Order arg) { arg.removeCustomer(this); }

92 Refactoring Change Unidirectional Association to Bidirectional: Mechanics: Add a field for the back pointer decide which class will control association create a helper method if needed modify existing modifier with back pointers if existing modifier call from existing modifiers.

93 Refactoring Summary: You have two-way association but one class no longer needs features from the other. Drop the unneeded end of the association. Change Bidirectional Association to Unidirectional

94 Refactoring Change Bidirectional Association to Unidirectional: Motivation: Bidirectional associations carry a price of added complexity of maintaining the two way connection and ensuring that objects are created and removed properly. They force an interdependency between the two classes. If they are too costly, remove them.

95 Refactoring Change Bidirectional Association to Unidirectional: class Order… // controlling methods void getCustomer () return _customer; } void setCustomer (Customer arg) { _customer = arg;} if (_customer is null) _customer.friendOrders().add(this); } // end setcustomer private Customer )customer; class Customer.. void addOrder (Order arg) { arg.setCustomer(this); } private Set )orders = new HashSet(); set FriendsOrders() { return _orders; }

96 Refactoring Change Bidirectional Association to Unidirectional: class Order… // controlling methods double getDiscountedPrice () { return getGrossPrice() * _customer.getDiscount()); } // end getDiscountedPrice class Order… double getDiscounted|Price (Customer customer) { return getGrossPrice) * (1- customer.getDiscount()); } // end getDiscountedPrice This reference Becomes this

97 Refactoring Change Bidirectional Association to Unidirectional: class Customer… double getPriceFor (Order order) { Assert.isTrue ()orders.contains(order)); return order.getDiscountedPrice*(); } // end getPriceFor class Customer… double getPriceFor (Order order) { Assert.isTrue(_orders.contains(order)); return order.getDiscountedPrice(this); } // end getPriceFor This reference Becomes this

98 Refactoring Change Bidirectional Association to Unidirectional: Mechanics: examine pointers if removal feasible use self encapsulate field if needed define accesss when no reader left remove all updates

99 Refactoring Summary: You have a class with many equal instances that you want to replace with a single object. Turn the object into a reference object. Change Value to Reference

100 Refactoring Change Value to Reference: Motivation: When you want to give a feature some changeable data and ensure that the change ripples to everyone referring to the object you need to turn it into a reference object.

101 Refactoring Change Value to Reference: class Customer { public Customer (String name) {_name = name;} public String getName() { return _name;} private final String _name; class Order…. public Order (String customerName) {_customer = new Customer (customerName);} public void setCustomer (String customerName) { _customer – new Customer (customerName); }// end setCustomer public String getCustomerName() { return )customer.getName(); } private Customer _customer; It is used by the order class.

102 Refactoring Change Value to Reference: private static int numberOfOrdersFor (Collection orders, String customers) { int result = 0; Iterator iter – orders.iteration(); while (iter.hasNext()) { // this routine counts the orders Order each = (Order) iter.next(); if (each.getCustomerName().equals(customer)) result++; } // end while return result }// end numberOfOrdersFor And some client code

103 Refactoring Change Value to Reference: class Customer { public static Customer create (String name) { return new Customer (name); } class Order { public Order (String customer) { _customer = Customer.create(customer); } class Customer { private Customer (String name) { _name = name; } private static Dictionary _instances = new Hashtable(); Begin with a factory method constructor. Replace constructor calls to factory calls. Make the constructor private. Select a method to access the customers.

104 Refactoring Change Value to Reference: Mechanics: use Replace Constructor with Factory Method decide what object is responsible for providing access decide if objects are precreated or created on the fly alter the factory method to return the reference object compile and test

105 Refactoring Summary: You have a reference object that is samll, immutable, and awkward to manage. Turn it into a value objects. Change Reference to Value

106 Refactoring Change Reference to Value: Motivation: If memory links become awkard, value objects are appropriate. Remember value objects should be immutable.

107 Refactoring Change Reference to Value: Example: class Currency private String _code; public String getCode() { return _code;} private Currency (String code) { _code = code; } Currency usd = Currency.get(“USD”); New Currency (“USD”).equals(new Currency (“USD”)) // returns false To get an instance you do the following: Note the constructor is private.

108 Refactoring Change Reference to Value: Example: public boolean equals (Object arg) { if (! (arg instanceof Currency)) return false; Currency other - )Currency) arg; return (_code.equals (other._code)); public int hashCode() { return _code.hashCode(); } New Currency (“USD”).equals (new Currency (“USD”)) Define an equals method. Define a hashCode. Define a new constructor.

109 Refactoring Change Reference to Value: Mechanics: check that object is or can be immutable create an equals method and a hash method compile and test consider removing any factory constructor

110 Refactoring Summary: A method needs more information from its caller. Add a parameter for an object that can pass on this information. Add Parameter

111 Refactoring Add Parameter: Motivation: You have a change a method, and the changes requires information that wasn’t passed in before, so you add a parameter.

112 Refactoring Add Parameter: Customer ____________ getContact () Customer ____________ getContact ( :Date)

113 Refactoring Add Parameter: Mechanics: Check to see method implemented by sub or super class. If it is do these steps for every implementation. Declare a new method with added parameter and copy the old body of code to the new method. Change the body of the old method so that it calls the new one. Find all references to the old method and change them to refer to the new one Remove the old method Compile and test.

114 Refactoring Summary: A parameter is no longer used by the method body. Remove it. Remove Parameter

115 Refactoring Remove Parameter: Motivation: Be not removing the parameter you make further work for everyone who uses the method.

116 Refactoring Remove Parameter: Customer ____________ getContact ( :Date) Customer ____________ getContact ( )

117 Refactoring Remove Parameter: Mechanics: Check to see whether signature is implemented by a super or sub class. If it does, don’t do this refactoring. Declare a new method without the parameter and copy the old body of code to the new method. Change the body of old metod so that it calls the new one. Find all references to old method to change them to refer to the new one Remove the old method. Compile and test

118 Refactoring Summary: Several methods do similar things (the code looks very similar) but it uses different values for some of the variables contained in the method body. Create one method that uses parameter(s) for the different variable values. Parameterize Method

119 Refactoring Parameterize Method: Motivation: You may need a few methods to do similar things (have the same code) but these methods vary depending on a few values. Replace the separate methods with a single method that handles the variations with parameters.

120 Refactoring Parameterize Method: class Employee { valid tenPercentRaise () { salary *= 1.1; } void fivePercentRaise () { salary *= 1.05; } // note here that both these method have similar code except for the magic numbers.1 and.05 // we should make variables out of the magic numbers and pass them to the method. void raise (double factor) { salary *= (1 + factor); } A very simple example is Is replaced with

121 Refactoring Parameterize Method: protected Dollars baseCharge () { double result = Math.min (lastUsage(), 100) * 0.03; if (lastUsage () > 100 ) { result += (Math.min (lastUsage (), 200) – 100) * 0.05; } // similarity if (lastUsage() > 200) { result += (lastUsage() – 200) * 0.07; } // note here similarity return new Dollars (result); protected Dollars baseCharge () { double result = usageInRange (0,100) * 0.03; result + = usageInRange (100, 200) * 0.05; result + = usageInRange (200, Integer.MAX_VALUE) * 0.07; return new Dollars (result); } // end baseCharge protected int usageInRange (int start, int end) { if lastUsage() > start) return Math.min (lastUsage(), end) – start; else return 0;} A less obvious case Is replaced with

122 Refactoring Parameterize Method: Mechanics: Create a parameterized method that can be substituted for each repetitive method. Replace one old method with a call to the new method. Repeat for all methods Compile and test.

123 Refactoring Summary: You have a method that runs different code depending on the values of an enumerated parameter. Create a separate method for each value of the parameter. Replace Parameter with Explicit Methods

124 Refactoring Replace Parameter with Explicit Methods: Motivation: You have discrete values of a parameter, test for those values in a conditional, and do different things. The method caller has to decide what it wants to do by setting the parameter, so you might as well provide different methods and avoid the conditional. The clarity of the explicit interface can be worthwhile even when the compile time checking isn’t an advantage.

125 Refactoring Replace Parameter with Explicit Methods: void setValue (String name, int value) { // method caller must know how method works if (name.equals (“height”)) _height = value; if (name.equals (“width”)) _width = value; Assert.shouldNeverReachHere(); } // void setHeight (int, arg) {_height = arg; } void setWidth (int arg) { _width = arg; } Given the following where caller must know if it is height or width GOES TO

126 Refactoring Replace Parameter with Explicit Methods: Mechanics: Create an explicit method for each value of the parameter. For each leg of the conditional, call the appropriate new method. Compile and test after changing each leg. Replace each caller of the conditional method with a call to the appropriate new method. With all callers changed, remove the conditional method Compile and test.=

127 Refactoring Summary: You are getting several values from an object and passing these values as parameters in a method call. Send the whole object instead. Preserve Whole Object

128 Refactoring Preserve Whole Object: Motivation: When an object passes several data values from a single object as parameters in a method call, you should consider passing the whole object from which the data came.

129 Refactoring Preserve Whole Object: int low = daysTempRange().getLow(); int high = daysTempRange().getHigh(); withinPlan = plan.withinRange(low,high); withinPlan = plan.withinRange(daysTempRange()); // send daysTempRange object Given GOES TO

130 Refactoring Preserve Whole Object: class Room… boolean withinPlan (HeatingPlan plan) { int low = daysTempRange().getLow(); int high = daysTempRange().getHigh(); return plan.withinRange(low,high); } // end withinPlan Class HeatingPlan… boolean withinRange (int low, int high) { return (low >= _range.getLow() && high <= _range.getHigh(); } // end withinRange private TempRange _range; A room objects records high and low temp during the day. Need to compare this range with a predefined heating plan.

131 Refactoring Preserve Whole Object: class HeatingPlan…. boolean withinPlan (TempRange roomRange, int low, int high) { return (low >= _range.getLow() && high <= _range.getHigh()); } // end within plan Class Room.. boolean withinPlan (HeatingPlan plan) { int low = daysTempRange().getLow(); int high = daysTempRange().getHigh(); return plan.withRange(daysTempRange(), low, high); } // end withinPlan Rather than unpack range information when passed, pass the whole range object. First add whole object as a parameter.

132 Refactoring Preserve Whole Object: class Room.. boolean withinPlan (HeatingPlan plan) { int low = daysTempRange().getLow(); int high = daysTempRange().getHigh(); return plan.withRange(daysTempRange()); } // end withinPlan class HeatingPlan boolean withinRange (TempRange roomRange) { return (roomRange.getLow() >= _range.getLow() && roomRange.getHigh() <= _rante.getHigh()) } // end withinRange Then I use method on the whole object instead of params

133 Refactoring Preserve Whole Object: class Room.. boolean withinPlan (HeatingPlan plan) { int low = daysTempRange().getLow(); int high = daysTempRange().getHigh(); return plan.withRange(daysTempRange*(, low, high); } // end withinPlan class HeatingPlan… boolean withinRange (TempRange roomRange) { return (_range.includes (roomRange)); } class TempRange.. boolean includes (TempRange arg) { return arg.getLow*( >= this.getLow() && arg.getHigh() <= this.getHigh(); } // end includes Now I can eliminate the temps Use whole object

134 Refactoring Preserve Whole Object: Mechanics: Create a new parameter for the whole object from which the date comes. Determine which parameters should be obtained from the whole object. Take one parameter and replace references to it within the method body by invoking an appropriate method on the whole object parameter. Delete the parameter Repeat for each parameter that can be got from the whole object. Remove the code in the calling method that obtaines the deleted parameters Compile and test.

135 Refactoring Summary: An object invokes a method, then passes the result as a parameter for a method. The receiver can also invoke this method. Remove the parameter and let the receiver invoke the method. Replace Parameter with Method

136 Refactoring Replace Parameter with Method: Motivation: If a method can get a value that is passed in as parameter by another means, it should. Long parameter lists are difficult to understand, and we should reduce them as much as possible.

137 Refactoring Replace Parameter with Method: int basePrice = _quantity * _itemPrice; discountLevel = getDiscountLevel(); double finalPrice = discountedPrice (basePrice, discountLevel); int basePrice = _quantity * _itemPrice; double finalPrice = discountedPrice (basePrice); GOES TO Given

138 Refactoring Replace Parameter with Method: public double getPrice () { int basePrice = _quantity * _itemPrice; int discountLevel; if (_quantity > 100) discountLevel = 2; else discountLevel = 1; double finalPrice = discountedPrice (basePrice, discountLevel); return finalPrice; } // end getPrice private double discountedPrice (int basePrice, int discountLevel) { if (discountLevel == 2) return basePrice * 0.1; else return basePrice * 0.05; } // end discountedPrice A variation on discounting orders is as follows

139 Refactoring Replace Parameter with Method: private double discountedPrice (int basePrice, int discountLevel) { if (getDiscountLevel == 2) return basePrice * 0.1; else return basePrice * 0.05; } // end discountedPrice public double getPrice*( { int basePrice = _quantity * _itemPrice; int discountLevel = getDiscountLevel(); double finalPrice = discountedPrice (basePrice); return finalPrice; private double discountedPrice (int basePrice) { if (getDiscountLevel() == 2) return basePrice * 0.1; else return basePrice * 0.05; } // end discountedPrice Replace references to the parameter in discountedPrice Remove parameter

140 Refactoring Replace Parameter with Method: public double getPrice() { int basePrice = _quantity * _itemPrice; double finalPrice = discountedPrice (basePrice); return finalPrice; public double getPrice() { return discountedPrice (); } private double discountedPrice (int basePrice) { if (getDiscountLevel() == 2) return getBasePrice() * 0.1; else return getBasePrice() * 0.05; } // end discountedPrice private double discountedPrice () { if (getDiscountLevel() == 2) return getBasePrice() * 0.1; else return getBasePrice() * 0.05; } // end discountedPrice private double getBasePrice() { return )quantity * _itemPrice; } Eliminate temp Eliminate other parameter and its temp

141 Refactoring Replace Parameter with Method: private double getPrice() { if (getDountLevel() == 2) return getBasePrice() * 0.1; else return getBasePrice() * 0.05; } // end getPrice Now use Inline method on discountedPrice

142 Refactoring Replace Parameter with Method: Mechanics: If necessary, extract the calculation of the parameter into a method. Replace references to the parameter in method bodies with references to the method. Use Remove Parameter on the parameter. Compile and test.

143 Refactoring Summary: You have a group of parameters that naturally go together. Replace them with an object. Introduce Parameter Object

144 Refactoring Introduce Parameter Object Motivation: A particular group of parameters that tend to be passed together may have several methods in one class or several class may use this group. This cluster of classes is a data clump and can be replaced with an object that carries all this data.

145 Refactoring Introduce Parameter Object Customer ____________ amountInvoicedIn(start: Date, end: Date) amountReceivedIn(start: Date, end: Date) amountOverdueIn (start: Date, end:Date) Customer ____________ amountInvoiceIn (DateRange) amountReceivedIn(DateRange) amountOverdureIn(DateRange)

146 Refactoring Introduce Parameter Object class Entry… Entry (double value, Date chargeDate) { _value = value; _chargeDate = chargeDate; } // end Entry Date getDate () { return _chargeDate; } double getValue() { return _value; } private Date _chargeDate; private double _value; Begin with an account and entries of simple data holders.

147 Refactoring Introduce Parameter Object class Account… double getFlowBetween (Date start, Date end); { double result = 0; Enumeration e = _entries.elements(); while (e.hasMoreElements()) { Entry each = (Entry) e.nextElements(); if (each.getDate().equals (start) || each.getDate().equals(end) || (each.getDate().after(start) && each.getDate().before(end))) { result += each.getValue(); return result; } // end while private Vector _entries = new Vector(); Client code….. double flow = anAccount.getFlowBetween(startDate, endDate) Focus on the account which holds a collection of entries.

148 Refactoring Introduce Parameter Object class DateRange { DateRange (Date start, Date end) { _start = start; _ end = end; } Date getStart() { return _start; } Date getEnd() { return _end; } private final Date _start; private final Date _end; } // end DateRange Declare a simple data holder for this range

149 Refactoring Introduce Parameter Object class Account… double getFlowBetween (Date start, Date end, DateRange range); { double result = 0; Enumeration e = _entries.elements(); while (e.hasMoreElements ()); while (e.hasMoreElements()) { Entry each = (Entry) e.nextElements(); if (each.getDate().equals (start) || each.getDate().equals(end) || (each.getDate().after(start) && each.getDate().before(end))) { result += each.getValue(); return result; } // end while Client code….. double flow = anAccount.getFlowBetween(startDate, endDate, null) Add date range into parameter list for getFlowBetween method

150 Refactoring Introduce Parameter Object class Account… double getFlowBetween ( DateRange range); { double result = 0; Enumeration e = _entries.elements(); while (e.hasMoreElements()) { Entry each = (Entry) e.nextElements(); if (each.getDate().equals (range.getStart()) || each.getDate().equals(range.getEnd) || (each.getDate().after(range.getStart()) && each.getDate().before(range.getEnd()))) { result += each.getValue(); return result; } // end while Client code….. double flow = anAccount.getFlowBetween(startDate, endDate) Remove params and use new object – delete start param and modify method and its callers to use new object method

151 Refactoring Introduce Parameter Object class Account… double getFlowBetween ( DateRange range); { double result = 0; Enumeration e = _entries.elements(); while (e.hasMoreElements()) { Entry each = (Entry) e.nextElements(); if (range.includes(each.getDate()) { result += each.getValue(); return result; } // end while class DateRange boolean includes (Date arg) { return (arg.equals (_start) || arg.equals (_end) || (arg.after(_start) && arg.before (_end))); } // end DateRange Now, in condition use extract method and move method.

152 Refactoring Introduce Parameter Object Mechanics: Create a new class to represent the group of parameters you are replacing and make the class immutable. Use Add Parameter for new data clump. Use a null for this parameter in all the callers. For each parameter in the data clump, remove the parameter from the signature. Modfiy the callers and metho body to use the parmeter object for that value. When you have removed the parameters, loook for behavior that you can move into the parameter object with move method Compile and test.

153 Refactoring How to handle exceptions is an art into itself. These are just a few refactorings. Exceptions

154 Refactoring Summary: A method returns a special code to indicate an error. Throw an exception instead. Replace Error Code with Exception

155 Refactoring Replace Error Code with Exception Motivation: Things can go wrong, the simplest case can stop a program with an error code. Java has a better way, exceptions. They clearly separate normal processing from error processing.

156 Refactoring Replace Error Code with Exception : int withdraw (int amount) { if (amount > _balance) return –1; else { _balance -= amount; return 0; } void withdrawn (int amount) throws BalanceException{ if (amount > _ balance) throw new BalanceException(); // exception here _balance -= amount; } // end withdrawn Given GOES TO

157 Refactoring Replace Error Code with Exception class Account… int withdraw (int amount) { if (amount > -balance) return –1; else _balance -= amount; return 0; } // end withdraw } // end Account private int )balance; If we withdraw more than your balance

158 Refactoring Replace Error Code with Exception if (account.withdraw(amount) == -1 handleOverdrawn*(; else doTheUsualThing(); if (!account.canWithdraw(amount)) handleOverdrawn(); else { account.withdraw(amount); doTheUsualThing(); } void withdraw (int amount { if (amount > )balance) throw new IllegalArgumentException (“Amount too large”); _balance -= amount; } // end withdraw Expect the caller to do the checking – use return code Replace the above with the following Use guard clause for condition check

159 Refactoring Replace Error Code with Exception class Account… void withdraw (int amount) { Assert.isTrue (“Amount too large”, amount > _balance); _balance -= amount; } // end withdraw class Assert… static void isTrue (String comment, boolean test) { if (! Test) { throw new RuntimeException (“Assertion failed: “ + comment); } } // end isTrue Signal the assertion

160 Refactoring Replace Error Code with Exception try { account.withdraw (amount); doTheUsualThing(); } catch (BalanceException e) { handleOverdrawn(); } void withdraw (int amount) throws BalanceException { if (amount > _balance) throw new BalanceException (); _balance -= amount; } // end withdraw Adjust the callers Change the withdraw method

161 Refactoring Replace Error Code with Exception if (account.withdraw(amount) == -1) handleOverdrawn(); else doTheUsualTHing(); class Account… int withdraw (int amount) { if (amount > _balance) return –1; else { _balance -= amount; return 0; } } // end withdraw void newWithdraw (int amount) throws BalanceException { if (_amount > _balance) throw new BalanceException(); _balance -= amount; Use a temporary intermediate method Create a new withdraw method using the exception.

162 Refactoring Replace Error Code with Exception int withdraw (int amount) { try { newWithdraw (amount); return 0; } catch (BalanceException e) { return –1; } } // end withdraw try{ account.newWithdraw (amount); catch (BalanceException e) { handleOverdrawn(); } Adjust the current withdraw method to use the new one Replace each of the calls to the old method with a call to new one

163 Refactoring Replace Error Code with Exception Mechanics: Decide whether the exception whould be checked or unchecked. Find all the callers and adjust them to use the exception. Change the signature of the method to reflect the new usage. With many callers, you can make the change more gradual as: Decide whether the exception would be checked or unchecked. Creae a new method that uses the exception. Modify the body of the old method to call the new method. Adjust each caller of the old method to call the new method Delete the old method Compile and test.

164 Refactoring Summary: You are throwing a checked exception on a condition the caller could have checked first. Change the caller to make the test first. Replace Exception with Test

165 Refactoring Replace Exception with Test Motivation: Exceptions can be used to excess. They should not act as a substitute for conditional tests.

166 Refactoring Replace Exception with Test double getValueForPeriod (int periodNumber) { try { return )values (periodNumber);} catch (ArrayIndexOutOfBoundsException e) { return 0; } } // end getValueForPeriod double getValueforPeriod (int periodNumber) { if (periodNumber >= )values.length) return 0; return )values [periodNumber]; } // end getValueForPeriod Given the following GOES TO

167 Refactoring Replace Exception with Test class ResourcePool Resource getResource() { Resource result; try { result = (Resource) _available.pop(); _allocated.push(result); return result; } catch (EmptyStackException e) { result = new Resource(); _allocated.push (result); return result; } } // end getResource Stack _available; Stack –allocated; A method for giving out resources might be as follows

168 Refactoring Replace Exception with Test Resource getResource() { Resource result; if (_available.isEmpty()) result = new Resource (); _allocated.push (result); return result; } // end if else { try { result = (Resource) _available.pop(); _allocated.push(result); return result;} catch (EmptyStackException e) {_allocated.push(result); return result; } } // end else } // endgetResource Add an appropriate up-front test and do the empty behavior

169 Refactoring Replace Exception with Test Resource getResource() { Resource result; if (_available.isEmpty()) {result = new Resource (); _allocated.push (result); return result; } // end if else { try { result = (Resource) _available.pop(); _allocated.push(result); return result;} catch (EmptyStackException e) { Assert.shouldNeverReachHere(“available was empty on pop”); result = new Resource();_allocated.push (result); } // end catch } // end else } // endgetResource Class Assert…. Static void shouldNeverReachHere (String message) { throw new RuntimeException (message); } Add an assertion to check this

170 Refactoring Replace Exception with Test Resource getResource() { Resource result; if (_available.isEmpty()) {result = new Resource (); _allocated.push (result); return result; } // end if else { result = (Resource) _available.pop(); _allocated.push(result); return result;} Resource getResource() { Resource result; if (_available.isEmpty()) {result = new Resource (); _allocated.push (result); return result; } // end if else { result = (Resource) _available.pop(); _allocated.push(result); return result;} Remove the try block completely Clean up the conditional code

171 Refactoring Replace Exception with Test GOES TO Bird ____________ getSpeed Bird ____________ getSpeed Bird ____________ getSpeed Bird ____________ getSpeed

172 Refactoring Replace Exception with Test Suppose you have the following inheritance structure Employee Type ENGINEER SALESMAN MANAGER Employee

173 Refactoring Replace Exception with Test 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

174 Refactoring Replace Exception with Test int getType () { return _type.getTypeCode(); } private EmployeeType _type; abstract class EmployeeType…. abstract int getTypeCode(); class Engineer extends EmployeeType… int getTypeCode() { return Employee.ENGINEER; } With the following code

175 Refactoring Replace Exception with Test class 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”); The case statement is extracted so I move it into employee type.

176 Refactoring Replace Exception with Test 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

177 Refactoring Replace Exception with Test 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

178 Refactoring Replace Exception with Test Mechanics: Put a test up front and copy the code from the catch block into the appropriate leg of the if statement. Add an assertion to the catch block to notify you whether the catch block is executed. Remove the catch block and the try block if there are not other catch blocks. Compile and test

179 Refactoring There are a few refactorings that have to do with refactoring for design patterns. Here are just a few. More is expected to evolve. Patterns

180 Refactoring Summary: You have domain data available only in a GUI control, and domain methods need access. Copy the data to a domain object. Set up an observer to synchronize the two pieces of data. Duplicate Observed Data

181 Refactoring Duplicate Observed Data: Motivation: you have a cluster of code which can be grouped into a method. turn the cluster in to a method with a name that explains the function of the method

182 Refactoring Duplicate Observed Data: Example: public class IntervalWindow extends Frame… java.awt.TextField _startfield, _endField, _lengthField; Class SymFocus extends java.awt.event.FocusAdapter { public void focusLost (java.awt.event.focusEvent event) { Object object=event.getSource(); if (object== _startField) StartField_FocusLost (event); else if (object == _endField) EndField_FocusLost (event); else if(object == _lengthField) LengthField_FocusLost(event); } } // end focusLost }// end SymFocus Start End Length Simple GUI

183 Refactoring Duplicate Observed Data: Example: void Startfield_FocusLost (java.awt.event.FocusEvent event) { if ( isNotInteger(_startField.getText() ) ) _startField.setText(“0”); calculateLength(); } // end StartField_FocusLost void Endfield_FocusLost (java.awt.event.FocusEvent event) { if ( isNotInteger(_endField.getText() ) ) _endField.setText(“0”); calculateLength(); } // end EndField_FocusLost void Lengthfield_FocusLost (java.awt.event.FocusEvent event) { if ( isNotInteger(_lengthField.getText() ) ) _lengthField.setText(“0”); calculateEnd(); } // end LengthField_FocusLost Start End Length Simple GUI

184 Refactoring Duplicate Observed Data: Example: void calculateLength() { try { int start = Integer.parseInt(_startField.getText()) int end = Integer.parseInt (_endField.getText()) int length = end – start; _lengthField.setText(String.validOf(length)); } // end try catch (NumberFormatException e) { throw new RuntimeException (“Error”);} void calculate End() { try { int start = Integer.parseInt(_startField.getText()) int length = Integer.parseInt (_lengthField.getText()) int end = length + start; _endField.setText(String.validOf ( end)); } // end try catch (NumberFormatException e) { throw new RuntimeException (“Error”);} Start End Length Simple GUI

185 Refactoring Duplicate Observed Data: Example: class Interval extends Observable() private Interval _subject; _subject = new Interval(); _subject.addObserver(this); update( _subject.null); public void update (Observable observed, Object arg) { ……} Start End Length Goal: separate non-visual logic (business object) from GUI Create a domain-class Create domain-class reference in source class Intialize fields and make window an observer Create an update method

186 Refactoring Duplicate Observed Data: Example: String getEnd() { return _endField.getText(); } void setEnd (String arg) { _endField.setText(arg); } void calculateLength() { try{ int start = Integer.parseInt(_startField.getText()); int end = Integer.parseInt (getEnd()); int length = end – start; _lengthField.setText(String.valueOf(length)); } // end try throw new RuntimeException (“Error”)} // end calculateLength Start End Length Replace references with accessors

187 Refactoring Duplicate Observed Data: Example: void calculateEnd() { try{ int start = Integer.parseInt(_startField.getText()); int length = Integer.parseInt (_lengthField.getText()); int end = length + start; _setEnd(String.valueOf(end)); } // end try throw new RuntimeException (“Error”)} // end calculateLength void endField_FocusLost (java.awt.event.FocusEvent event) { if (isNotInteger(getEnd()) setEnd (“0”) calculateLength(); } // end endField Start End Length

188 Refactoring Duplicate Observed Data: Example: class Interval… private String )end + “0”; class Interval…. String getEnd() { return )end; } void setEnd (String arg) { )end + arg; setChanged(); notifyObservers*)(; } // end Inteval Start End Length Add end field to domain class with same initialization Add get and set methods.

189 Refactoring Duplicate Observed Data: Example: class IntervalWindow…… String getEnd() { return _subject.getEnd(); } void setEnd (String arg) { _subject.setEnd(arg); } class IntervalWindow… public void update (Observable observed, Object arg) { _endField.setText (_subject.getEnd()); } // end update Start End Length Update the accessors. Update the update method to ensure GUI reacts

190 Refactoring Duplicate Observed Data: Mechanics: make the presentation class an observer use Self Encapsulate Field on domain data add call to setting method define the data and accessor methods redirect the accessors to write to the domain field modify the observers update method compile and test.

191 Refactoring Summary: You want to do more than simple construction when you create an object Replace the constructor with a factory method. Replace Constructor with Factory Method

192 Refactoring Replace Constructor with Factory Method Motivation: The most obvious modification for this refactoring, comes with replacing a type code with subclassing. Since constructors only returns an instance of object requested, we need to replace a constructor with a factory method You can use factory methods for other situations which construction are limited.

193 Refactoring Replace Constructor with Factory Method Employee (int type) { _type = type } Static Employee create (int type) { return new Employee (type); } Given GOES TO

194 Refactoring Replace Constructor with Factory Method 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 } Static Employee create (int type) { return new Employee (type); } An example is the employment payment system Create the factory method

195 Refactoring Replace Constructor with Factory Method Client code… Employee eng = Employee.create (Employee.ENGINEER); class Employee…. private Employee (int type) { _type = type } // temp solution Change all callers to use new constructor

196 Refactoring Replace Constructor with Factory Method 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 code value”); } // end switch } // end create Apply replace type code with subclasses and hide from clients

197 Refactoring Replace Constructor with Factory Method static Employee create (String name) { try { return (Employee) Class.forName (name).newInstance(); } catch (Exception e) {throw new IllegalArgumentException (“Unable to instantiate” + name);} } // end create Class Employee { static Employee create (int type) { switch (type) { case ENGINEER: return create (“Engineer”); case SALESMAN: return create (“Salesman”); case MANAGER: return create (“Manager”); default: throw new IllegalArgumentException (“Incorrect type code value”); } // end switch } // end create Create a new method that takes a string AND use the method

198 Refactoring Replace Constructor with Factory Method Employee.create(ENGINEER);Employee.create (“Engineer”); class Person… static Person createMale() { return new Male(); } static Person createFemale() { return new Female (); } Person kent = new Male();Person kent = Person.createMale(); Change caller statements from….. to Another approach hides subclasses with explicit methods. Change caller statements from ……. to

199 Refactoring Replace Constructor with Factory Method Mechanics: Create a factory method. Make its body a call to the current constructor. Replace all calls to the constructor with calls to the factory method. Declare the constructor private. Compile and test.

200 Refactoring Summary: You have a type code that affects the behavior of a class, but you cannot use subclassing. Replace the type code with a state object. Replace Type Code with State Strategy

201 Refactoring Replace Type Code with State Strategy: Motivation: This technique is used when you have a type code that affects the behavior of a class but the type code changes during the life of the object or if another reason prevents subclassing. It uses either the state or strategy pattern.

202 Refactoring Replace Type Code with State Strategy: 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;}

203 Refactoring Replace Type Code with State Strategy: int payAmount () { switch (_type) { case ENGINEER: return _monthlySalary; case SALESMAN: return _monthlySalary + _commission; case MANAGER: return _monthlySalary + _bonus; default: throw new RuntimeException (“Incorrect employee”); } // end case } // end create Conditional code that would use these codes.

204 Refactoring Replace Type Code with State Strategy: Employee (int type) { setType (type);} void setType (int arg) { _type = arg; } int payAmount () { switch (getType()) { case ENGINEER: return _monthlySalary; case SALESMAN: return _monthlySalary + _commission; case MANAGER: return _monthlySalary + _bonus; default: throw new RuntimeException (“Incorrect employee”); } // end case } // end create With the type mutable, thus self encapsulate type code.

205 Refactoring Replace Type Code with State Strategy: class Engineer extends EmployeeType { int getTypeCode () { return Employee.ENGINEER } } // end Engineer class Manager extends EmployeeType { int getTypeCode () { return Employee.MANAGER } } // end Manager class Salesman extends EmployeeType { int getTypeCode () { return Employee.SALESMAN } } // end Salesman Declare the state class, as an abstract class and provide abstract method for returning type code.

206 Refactoring Replace Type Code with State Strategy: class Employee… void setType (int arg) { _type = EmployeeType.newType(arg); } class EmployeeType….. 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 (“Incorrect employee code”); } // end case } // end create Get the switch statements down to the minimum. Use replace constructor with factory for different cases. Eliminate other use cases with replace conditional with Polymorphism. Copy type code definitions into employee type, create factory method for types, and adjust setting method on employee.

207 Refactoring Replace Type Code with State Strategy: 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 lRuntimeException (“Incorrect employee”); } // end case } // end create Remove type code definitions from the employee and replace them with references to the employee type.

208 Refactoring Replace Type Code with State Strategy: Mechanics: Self encapsulate the type code Create a new class and name it for types purpose Add subclasses of the state object Create an abstract query in state object Create field in old class for new state object Adjust type code query on original class Adjust type code setting methods Compile and test


Download ppt "Refactoring Refactoring plays a major role in the decisions of where to put responsibilities. While deciding where these responsibilities lie, a refactoring."

Similar presentations


Ads by Google