Title: Moving Features between Objects
1Moving Features between Objects
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.
2Moving Features between Objects
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.
3Moving Features between Objects
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
41. Move Method
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.
51. 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.
61. Move Method Exampleversion
0 Class Account double overdraftCharge()
if ( _type.isPremium()) double
result 10 if (_dayOverdrawn gt 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 gt 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
71. Move Method Example
version 1 Class AccountType several account
types diff method double overdraftCharge(int
daysOverdrawn) if (isPremium())
double result 10 if
(dayOverdrawn gt 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
(_daysOverdrawn gt 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
81. 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
91. 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
102. Move Field
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.
112. 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.
122. 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.
132. Move Field Example Class
AccountType. private double _interestRate
void setInterestRate (double arg) _interestRate
arg double getInterestRate () return
_interestRate . double interestForAccount_d
ays (double amount, int days) return
_type.getInterestRate() amount days/365 //
end interestForAccount_days
Move _interestRate.
In the original class
reference get and set methods for the field.
142. 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
155. Hide Delegate
Summary A client is calling a delegate class
of an object Create methods on the server to
hide the delegate.
165. Hide Delegate Motivation Encapsulation
is the key to minimizing the impact of
maintenance changes.
175. Hide Delegate
Client must know about Person and Department
Person must know about department
Client must know about Person Person must know
about department
Client Class
Client Class
Person getManager()
Person getDept()
Department getManager()
Department
185. 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().getMa
nager()
If a client wants to know a manager, it needs to
get the department first.
195. 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.
205. Hide Delegate Mechanics create needed
delegate methods adjust the client to call the
server compile and test
216. Remove Middle Man
Summary A class is doing too much simple
delegation. Get the client to call the
delegate directly.
226. 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.
236. Remove Middle Man
Client must know about Person Person must know
about department
Client must know about Person and Department
Person must know about department
Client Class
Client Class
Person getManager()
Person getDept()
Department getManager()
Department
246. Remove Middle Man Example class
Person. Department _department public
Person getManager () return _department.getManag
er( class Department private Person
_manager public Department (Person manager)
_manager manager manager
john.getManager()
Person is hiding the department.
256. 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.
266. 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
277. Introduce Foreign Method
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.
287. 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.
297. Introduce Foreign Method Example Date
newStart new Date (previousEnd.getYear(),
previousEnd.getMonth(), previousEnd.getDate()
1) Date newStart nextDat(previousEnd) priva
te static Date nextDay (Date arg) return
new Date (arg.getYear().arg.getMonth().arg.getDate
() 1) // end nextDay
becomes
307. 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
318. Introduce Local Extension
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.
328. 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.
338. Introduce Local Extension Example
SUBCLASS Class mfDate extends Date public
nextDay() public dayOfYear() WRAPPER cl
ass mfDate private Date _original
Need to decide if you wish to use a subclass
(inheritance) or a wrapper (delegation)
348. Introduce Local Extension Example
SUBCLASS Class MfDateSub extends Date
public mfDateSub (String dateString) super
(dateString)
Declare the sub class.
358. 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.
368. 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.
378. Introduce Local Extension Example
WRAPPER public int getYear() return
)original.getYear() public boolean equals
(myDateWrap arg) return (toDate().equals(arg.t
oDate()))
Delegate all methods of original class to the new
class a few here
388. 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.
398. 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.
408. 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.
418. 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.
42Building Classes
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.
43Replace Method with Method Object
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.
44Replace 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.
45Replace 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) gt 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.
46Replace 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.
- Declare new class Gamma.
- Provide a final field for original object -
Account. - Provide fields for each param and temp variables.
47Replace 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) gt
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.
48Replace 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) gt 100)
importantValue2 -20 // end importantThing
Modify the old method to delegate it to the
object.
Now, EXTRACT any important METHODS.
49Replace 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
50Replace Data Value with Object
Summary You have a data item that needs
additional data or behavior. Turn the data item
into an object.
51Replace 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.
52Replace 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.
53Replace Record with Data Class
Summary You need to interface with a record
structure in a traditional programming
environment. Make a dumb data object for the
record.
54Replace 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.
55Replace Record with Data Class Example
class Person String ssn String
firstName String lastName String
middleName // end Person
Person
56Replace Record with Data Class
Mechanics create a class to represent
record give the class private fields code get
and set methods for each data item compile and
test.
57Replace Type Code with Class
Summary A class has a numeric type code that
does not affect its behavior. Replace the
number with the new class.
58Replace Type Code with Class Motivation
Numeric Type codes, are used to identify types
of data and often to invoke particular behavior.
59Replace 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
60Replace 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
_valuesarg // end BloodGroup
Create new blood group class with instances
containing type code.
61Replace 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.
62I
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(BloodGro
up arg) _bloodGroup arg
Rename the method on the accessor
Add a getting method
Create a new constructor and setting method.
63I
Replace Type Code with Class Example
Person thePerson new Person(Person.A) Person
thePerson new Person (BloodGroup.A) thePers
on.getBloodGroupCode() thePerson.getBloodGroup().
getCode()
Change static variable references
becomes
References to the getting method need to use new
one. method.
becomes
64I
Replace Type Code with Class Example
thePerson.setBloodGroup(Person.AB) thePerson.setB
loodGroup(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
65I
Replace Type Code with Class Example
class BloodGroup //.. private int getCode()
return _code private static BloodGroup code
(int arg) return _valuesarg // end
BloodGroup
Privatize methods on blood group that use the code
66Replace 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.
67Replace 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
68Introduce Null Object
Summary You have repeated checks for a null
value. Replace the null value with a null
object.
69Introduce 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.
70Introduce 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
71Introduce 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
72Introduce Null Object public class
PaymentHistory. int getWeeksDeliquentInLastYea
r() 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().getWeeksDel
inquentInLastYear()
Payment history has its own features
Some features of customer
73Introduce 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
74Introduce 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
75Introduce Null Object Customercustomer
site.getCustomer() BillingPlan plan if
(customer.isNull()) plan BillingPlan.basic() el
se 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().getWeeksDel
inquentInLastYear()
Alter all uses of value so they test with
isNull() rather than null
76Introduce 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
77Introduce 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.
78Replace Array with Object
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.
79Replace 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.
80Replace Array with Object Example
String row new String3 row 0
Liverpool row 1 13 String name
row(0) int wins Integer.parseInt(row(1))
The original array.
Code using the array.
81Replace Array with Object Example
class Performance row._data0
Liverpool row._data 1 13 String
name row._data(0) int wins
Integer.parseInt(row._data(1))
Create and access the array.
82Replace Array with Object Example
class Performance public String getName()
return _data0 public void setName (String
arg) _data0 arg String name
row.getName() int wins Integer.parseInt(row.
_data1) private String .data new
String3
Add getters and setters.
Make the array private.
83Replace 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.
84Replace 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
85Parameters
Determining parameters for methods should be
guided by one underlying principle (minimal
coupling). This includes both method and class
coupling.
86Change Unidirectional Association to Bidirectional
Summary You have two classes that need to
use each others features, but there is only a
one-way link. Add back pointers, and change
modifiers to update both sets.
87Change 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
88Change 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
89Change 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(.remov
e(this) _customer arg if
(_customer is null) _customer.friendOrders().add(t
his) // end setCustomer
Allow order to take charge add helper method to
customer
Update the modifier to update back pointers
90Change 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
91Change Unidirectional Association to
Bidirectional void removeCustomer
(Customer arg) arg.friendOrders().remove(thi
s) _customers.remove(arg) // end
removecustomer class Customer.. void
addOrder(Order arg) arg.addCustomer(this)
void removeOrder (Order arg) arg.removeCustomer(
this)
92Change 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.
93Change Bidirectional Association to Unidirectional
Summary You have two-way association but one
class no longer needs features from the other.
Drop the unneeded end of the association.
94Change 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.
95Change 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
96Change Bidirectional Association to
Unidirectional class Order // controlling
methods double getDiscountedPrice ()
return getGrossPrice() _customer.getDiscount())
// end getDiscountedPrice class
Order double getDiscountedPrice (Customer
customer) return getGrossPrice) (1-
customer.getDiscount()) // end
getDiscountedPrice
This reference
Becomes this
97Change 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
98Change 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
99Change Value to Reference
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.
100Change 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.
101Change 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.
102Change 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
103Change 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.
104Change 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
105Change Reference to Value
Summary You have a reference object that is
samll, immutable, and awkward to manage. Turn it
into a value objects.
106Change Reference to Value Motivation If
memory links become awkard, value objects are
appropriate. Remember value objects should be
immutable.
107Change 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.
108Change 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.
109Change 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
110Add Parameter
Summary A method needs more information from
its caller. Add a parameter for an object that
can pass on this information.
111Add Parameter Motivation You have a change
a method, and the changes requires information
that wasnt passed in before, so you add a
parameter.
112Add Parameter
Customer ____________ getContact ()
Customer ____________ getContact ( Date)
113Add 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.
114Remove Parameter
Summary A parameter is no longer used by the
method body. Remove it.
115Remove Parameter Motivation Be not removing
the parameter you make further work for everyone
who uses the method.
116Remove Parameter
Customer ____________ getContact ( Date)
Customer ____________ getContact ( )
117Remove Parameter Mechanics Check to see
whether signature is implemented by a super or
sub class. If it does, dont 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
118Parameterize Method
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.
119Parameterize 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.
120Parameterize 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
121Parameterize Method protected Dollars
baseCharge () double result Math.min
(lastUsage(), 100) 0.03 if (lastUsage ()
gt 100 ) result (Math.min (lastUsage (), 200)
100) 0.05 // similarity if
(lastUsage() gt 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() gt start)
return Math.min (lastUsage(), end) start else
return 0
A less obvious case
Is replaced with
122Parameterize 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.
123Replace Parameter with Explicit Methods
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.
124Replace 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 isnt an advantage.
125Replace 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
126Replace 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.
127Preserve Whole Object
Summary You are getting several values from
an object and passing these values as parameters
in a method call. Send the whole object
instead.
128Preserve 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.
129Preserve 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
130Preserve 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 gt _range.getLow() high lt
_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.
131Preserve Whole Object class HeatingPlan.
boolean withinPlan (TempRange roomRange, int low,
int high) return (low gt _range.getLow()
high lt _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.
132Preserve 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() gt _range.getLow()
roomRange.getHigh() lt _rante.getHigh()) //
end withinRange
Then I use method on the whole object instead of
params
133Preserve 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( gt this.getLow()
arg.getHigh() lt this.getHigh() // end includes
Now I can eliminate the temps
Use whole object
134Preserve 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.
135Replace Parameter with Method
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.
136Replace 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.
137Replace Parameter with Method int basePrice
_quantity _itemPrice discountLevel
getDiscountLevel() double finalPrice
discountedPrice (basePrice, discountLevel)
int basePrice _quantity
_itemPrice double finalPrice discountedPrice
(basePrice)
Given
GOES TO
138Replace Parameter with Method public double
getPrice () int basePrice _quantity
_itemPrice int discountLevel if
(_quantity gt 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
139Replace 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
140Replace 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
141Replace 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
142Replace 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.
143Introduce Parameter Object
Summary You have a group of parameters that
naturally go together. Replace them with an
object.
144Introduce 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.
145Introduce Parameter Object
Customer ____________ amountInvoicedIn(start
Date, end Date) amountReceivedIn(start Date,
end Date) amountOverdueIn (start Date, endDate)
Customer ____________ amountInvoiceIn
(DateRange) amountReceivedIn(DateRange) amountOver
dureIn(DateRange)
146Introduce 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.
147Introduce 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.
148Introduce 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
149Introduce 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
150Introduce 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
151Introduce 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.
152Introduce 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.
153Exceptions
How to handle exceptions is an art into itself.
These are just a few refactorings.
154Replace Error Code with Exception
Summary A method returns a special code to
indicate an error. Throw an exception instead.
155Replace 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.
156Replace Error Code with Exception int
withdraw (int amount) if (amount gt
_balance) return 1 else _balance -
amount return 0 void withdrawn (int
amount) throws BalanceException if
(amount gt _ balance) throw new BalanceException()
// exception here _balance - amount //
end withdrawn
Given
GOES TO
157Replace Error Code with Exception class
Account int withdraw (int amount)
if (amount gt -balance) return 1
else _balance - amount return 0
// end withdraw // end Account private int
)balance
If we withdraw more than your balance
158Replace 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 gt )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
159Replace Error Code with Exception class
Account void withdraw (int amount)
Assert.isTrue (Amount too large, amount gt
_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
160Replace Error Code with Exception try
account.withdraw (amount) doTheUsualThing()
catch (BalanceException e)
handleOverdrawn() void withdraw (int
amount) throws BalanceException if
(amount gt _balance) throw new BalanceException
() _balance - amount // end withdraw
Adjust the callers
Change the withdraw method
161Replace Error Code with Exception if
(account.withdraw(amount) -1)
handleOverdrawn() else doTheUsualTHing() class
Account int withdraw (int amount)
if (amount gt _balance) return 1 else
_balance - amount return 0 // end
withdraw void newWithdraw (int amount) throws
BalanceException if (_amount gt _balance)
throw new BalanceException() _balance -
amount
Use a temporary intermediate method
Create a new withdraw method using the exception.
162Replace 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
163Replace 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.
164Replace Exception with Test
Summary You are throwing a checked exception
on a condition the caller could have checked
first. Change the caller to make the test first.
165Replace Exception with Test Motivation
Exceptions can be used to excess. They should
not act as a substitute for conditional tests.
166Replace Exception with Test double
getValueForPeriod (int periodNumber) try
return )values (periodNumber) catch
(ArrayIndexOutOfBoundsException e) return 0
// end getValueForPeriod double
getValueforPeriod (int periodNumber) if
(periodNumber gt )values.length) return
0 return )values periodNumber // end
getValueForPeriod
Given the following
GOES TO
167Replace 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
168Replace 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 (EmptyS