Download presentation
Presentation is loading. Please wait.
Published byMarshall Johns Modified over 9 years ago
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
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.