decide how to put responsibilities?
There are 8 methods including move field or move method, Extract Class vs. Inline Class,
Hide Delegate vs. Remove Middle Man, Introduce Foreign Method & Introduce Local Extension (subclass or wrapper).
2. Outline
• Moving Features Between Objects
Decide where to put responsibilities (refactoring?)
– Move Method vs. Move Field
– Extract Class vs. Inline Class
– Hide Delegate vs. Remove Middle Man
– Introduce Foreign Method & Introduce Local
Extension
• Summary
3. Move Method
Motivation: Class S Class S
In Class S, method() used more features from Class T
Mechanics:
• Examine all features used by the method on Class S. Method()
– Consider what situation should be move? (one or more methods ?)
• Check the sub- & superclasses of Class S
– unless Class S and Class T are both the polymorphism
• Naming the method’ in Class T (more sense)
Class T Class T
• Copy the method code from Class S to Class T. Adjust the method’
to fit Class T
– Reference back Class S? just send a parameter from Class S
– Exception handler at Class S or Class T? Method’()
• Compile Class T
• Determine how to reference back Class S
– Class T has existing field or method
– If no existing, create a method in Class T
– or Create a new field (store the target object) in Class S [temporariness]
• Turn the source method into a delegating method
• Compile and test
• Decide whether to remove the source method or retain it as a
delegating method
– retain as a delegating method => many references
– If remove the source method, replace all the references and make the
reference to link Class T
• Compile and test
4. Example for Move Method (1)
Original code Many accounts join class AccountType... Class T
Class S & Every account has double overdraftCharge(int daysOverdrawn) {
class Account...
double overdraftCharge() { self-overdraftCharge if (isPremium()) {
if (_type.isPremium()) { => refactoring double result = 10;
if (daysOverdrawn > 7)
double result = 10; the original code
if (_daysOverdrawn > 7) result += (daysOverdrawn - 7) * 0.85;
result += (_daysOverdrawn - 7) * 0.85; return result;
return result; }
} else return daysOverdrawn * 1.75;
else return _daysOverdrawn * 1.75; }
}
double bankCharge() { Remain delegation
class Account...
double result = 4.5; individual double overdraftCharge() {
if (_daysOverdrawn > 0) accounts return _type.overdraftCharge(_daysOverdrawn);
result += overdraftCharge(); }
return result;
}
private AccountType _type; class Account...
private int _daysOverdrawn; double bankCharge() {
double result = 4.5;
if (_daysOverdrawn > 0)
result += _type.overdraftCharge(_daysOverdrawn);
return result;
} a parameter
5. Example for Move Method (2)
class AccountType...
double overdraftCharge(int daysOverdrawn) {
if (isPremium()) { Reference
double result = 10; a parameter
if (daysOverdrawn > 7)
result += (daysOverdrawn - 7) * 0.85;
return result;
}
else return daysOverdrawn * 1.75;
} Reference
a field
(need several
class AccountType...
double overdraftCharge(Account account) { features)
if (isPremium()) {
double result = 10; If there are too
if (account.getDaysOverdrawn() > 7) many features
result += (account.getDaysOverdrawn() - 7) * 0.85;
=> future
return result;
} refactoring
else return account.getDaysOverdrawn() * 1.75;
}
6. Move Field
• Motivation Class S Class S
– Class S’s field(be used by more methods) is used frequently in
Class T
• If those methods seem sensible where they are => Move Field
– When doing Extract Class, must do Move Field
Field
• Mechanics
– Public field => use Encapsulate Field
• If the field be accessed frequently by many methods => use Self Class T Class T
Encapsulate Field
– Compile and test
– Create a field in Class T with getting & setting methods Field’
– Compile Class T
– Determine how to reference back Class S
• Class T has existing field or method
• If no existing, create a method in Class T
• or Create a new field (store the target object) in Class S
[temporariness]
– Remove Class S’s field
– Replace all field reference in Class S and chose the appropriate
link for Class T
• Access field is by variable => replace the reference with a call to the
target object’s getting method
• Access field by assignments => replace the reference with a call to the
setting method
• If field is not private => look in all subclass S for reference
– Compile and test
7. Example for Move field
class Account...
private AccountType _type;
private double _interestRate;
double interestForAmount_days (double amount, int days) {
return _interestRate * amount * days / 365;
}
a field
class AccountType...
private double _interestRate;
void setInterestRate (double arg) {
_interestRate = arg; accessor
}
double getInterestRate () {
pair
return _interestRate;
}
class Account...
private double _interestRate;
double interestForAmount_days (double amount, int days) {
return _type.getInterestRate() * amount * days / 365;
}
8. Example for Move field – Self Encapsulate Field
class Account...
private AccountType _type; a field
private double _interestRate;
double interestForAmount_days (double amount, int days) {
A lot of methods use
return getInterestRate() * amount * days / 365; the interest rate field
} => refactoring for
private void setInterestRate (double arg) { easy expanding class
_interestRate = arg; accessor
}
private double getInterestRate () {
pair
return _interestRate;
}
double interestForAmountAndDays (double amount, int days) {
return getInterestRate() * amount * days / 365;
} Asscessor =>
private void setInterestRate (double arg) {
_type.setInterestRate(arg);
Self Encapsulate
} Field (redirection)
private double getInterestRate () {
return _type.getInterestRate();
}
9. Extract Class
Person
• Motivation name
– A class is too big to understand easily officeAreaCode
• Many methods & a lot of data officeNumber
getTelephoneNumber
• Mechanics
– Decide how to split the responsibilities of the class
– Create a new class to express the split-off responsibility
• Maybe rename the old class
Person
– Make a link from the old to the new class
• May need a 2-way link name
– Use Move Field on each field you wish to move
getTelephoneNumber
– Compile and test after each move
– Use Move Method to move methods over from old to new
• Start with low-level methods(few call) & build to the higher level officeTelephone
– Compile and test after each move
– Review & reduce the interfaces of each class
• 2-way link => one way Telephone Number
– Decide whether to expose the new class areaCode
• as a reference object or as an immutable value object (ch.8) number
getTelephoneNumber
10. class Person...
public String getName() {
Example for Extract Class (1)
return _name;
} Move Field
public String getTelephoneNumber() { class TelephoneNumber {
return ("(" + _officeAreaCode + ") " + String getAreaCode() {
_officeNumber); return _areaCode;
} } accessor
String getOfficeAreaCode() { void setAreaCode(String arg) {
return _officeAreaCode; pair
accessor _areaCode = arg;
} }
void setOfficeAreaCode(String arg) { pair private String _areaCode;
_officeAreaCode = arg; }
}
String getOfficeNumber() { class Person...
return _officeNumber; public String getTelephoneNumber() {
} return ("(" + getOfficeAreaCode() + ")
void setOfficeNumber(String arg) { Intermediate " + _officeNumber);
_officeNumber = arg; process to }
} fool complier String getOfficeAreaCode() {
private String _name; return _officeTelephone.getAreaCode();
private String _officeAreaCode; accessor }
private String _officeNumber;
New pair void setOfficeAreaCode(String arg) {
class TelephoneNumber { _officeTelephone.setAreaCode(arg);
class
} }
class Person ...
private TelephoneNumber _officeTelephone = new TelephoneNumber();
11. Example for Extract Class (2)
class Person...
public String getName() {
return _name;
Move Method }
public String getTelephoneNumber(){
class TelephoneNumber... return
public String getTelephoneNumber() { _officeTelephone.getTelephoneNumber();
return ("(" + _areaCode + ") " + _number); }
} TelephoneNumber getOfficeTelephone() {
String getAreaCode() { return _officeTelephone;
return _areaCode; }
} private String _name;
void setAreaCode(String arg) { private TelephoneNumber _officeTelephone =
_areaCode = arg; new TelephoneNumber();
}
String getNumber() {
return _number;
}
void setNumber(String arg) {
_number = arg;
}
private String _number;
private String _areaCode;
12. Inline Class
absorbing class
• Motivation Person
– A class isn’t doing very much name
• Maybe the result of refactoring to move other officeAreaCode
responsibilities out of class officeNumber
• Mechanics (Move all its features into another getTelephoneNumber
class and delete it)
– Declare the public protocol of the source class
onto the absorbing class. Delegate all these source class
methods to the source class Person
• Source class methods have a separate interface => name
use Extract Interface(ch.11) before inlining
– Change all references (source class -> getTelephoneNumber
absorbing class)
• Source class: Declare private to out-of-package
reference & change name to fool compiler officeTelephone
– Compile and test
– Use Move Method and Move Field to move Telephone Number
features form the source class to the absorbing areaCode
class (until nothing is left) number
– Delete non-necessary class getTelephoneNumber
13. class Person...
public String getName() {
return _name;
Example for Inline class
}
public String getTelephoneNumber(){ Declare all the visible
return _officeTelephone.getTelephoneNumber(); methods about
} TelephoneNumber
TelephoneNumber getOfficeTelephone() { class Person...
return _officeTelephone;
String getAreaCode() {
}
return _officeTelephone.getAreaCode();
private String _name;
}
private TelephoneNumber _officeTelephone = new
void setAreaCode(String arg) {
TelephoneNumber();
_officeTelephone.setAreaCode(arg);
class TelephoneNumber... }
public String getTelephoneNumber() { String getNumber() {
return ("(" + _areaCode + ") " + _number); return _officeTelephone.getNumber();
} }
String getAreaCode() { void setNumber(String arg) {
return _areaCode; _officeTelephone.setNumber(arg);
} }
void setAreaCode(String arg) {
_areaCode = arg; interface
}
String getNumber() { Person martin = new Person();
return _number; martin.setAreaCode ("781");
}
void setNumber(String arg) {
_number = arg; interface
} Person martin = new Person();
private String _number; martin.getOfficeTelephone().setAreaCode ("781");
private String _areaCode;
14. Hide Delegate
Person
Client getDepartment
Class
Department
getMange
r
Client calls delegate class by server
object
Client Person
Department
Class getManager
Server builds all methods (delegate method) to be used by
client Server Delegate
Client
Method() Method()
Changes are limited to
Server-side & don’t Delegate.method() The advantage of
propagate to Client-side encapsulating
15. Hide Delegate - mechanics
• Mechanics
– Create a simple delegating method (for each
method) on the server
• Client is not the same package as server => make
the delegate method to visibility
– Compile and test after adjustung each method
– If no client needs to access the delegate
anymore, remove the server’s accessor for
the delegate
– Compile and test
16. Example for Hide Delegate
Original code …
class Person… client accesses a person's manager =>
Department _department; get the department first
public Department getDepartment() { manager = john.getDepartment().getManager();
return _department;
} Refactoring…
public void setDepartment(Department
arg) { class Person...
_department = arg; Department _department;
} public Person getManager() {
return _department.getManager();
class Department… }
private String _chargeCode;
private Person _manager; class Department...
public Department (Person manager) { private Person _manager;
_manager = manager; public Department (Person manager) {
} _manager = manager;
public Person getManager() { }
return _manager;
} client accesses a person's manager
manager = john.getManager();
17. Remove Middle Man
Client Get the caller to
Class A class has too Client call the delegate
much simple Class
Foo
directly
delegation
getImpValue
Foo Bar
Bar getbar getImpValue
• Motivation
– Add too much delegating on the server, so accessing
becomes painful => server class is a middle man
• Mechanics
– Create an accessor for the delegate
– For each client use of a delegate,
remove the method from server &
make the client call the delegate method directly
– Compile and test after each method
18. Example for Remove Middle Man(1)
Original code … Refactoring…
public class Foo { public class Foo {
Bar bar; Bar bar;
public Foo getImpValue(){ public Bar getbar() {
return bar.getImpValue(); return bar;
} }
} }
public class Bar { public class Bar {
private Foo impValue1; private Foo impValue1;
public Bar(Foo impValue){ public Bar(Foo impValue){
impValue1 = impValue; impValue1 = impValue;
} }
public Foo getImpValue(){ public Foo getImpValue(){
return impValue1; return impValue1;
} }
} }
public class Client { public class Client {
Foo a; Foo a;
Foo impValue = a.getImpValue(); Foo impValue = a.getbar().getImpValue();
} }
http://www.jetbrains.com/idea/webhelp/remove-middleman.html
19. Example for Remove Middle Man(2)
may have a set of “Ghost” classes in code public class Consumer {
public AccountDataProvider AccountDataProvider { get; set; }
public class Consumer { public Consumer(AccountDataProvider dataProvider) {
public AccountManager AccountManager { get; set; } AccountDataProvider = dataProvider;
public Consumer(AccountManager accountManager) { }
AccountManager = accountManager; public void Get(int id) {
} Account account = AccountDataProvider.GetAccount(id);
public void Get(int id) { }
Account account = AccountManager.GetAccount(id); }
}
} public class AccountDataProvider {
public Account GetAccount(int id) {
public class AccountManager { // get account
public AccountDataProvider DataProvider { get; set; } }
public AccountManager(AccountDataProvider }
dataProvider) {
DataProvider = dataProvider; Refactoring…
}
public Account GetAccount(int id) { “Ghost” classes
return DataProvider.GetAccount(id);
}
} public class AccountDataProvider {
public Account GetAccount(int id) {
// get account
}
} Original code …
http://lostechies.com/seanchambers/2009/08/28/refactoring-day-29-remove-middle-man/
20. Introduce Foreign Method
Need to add methods • Motivation
in server class, but – Want add foreign method in, but
can’t change the source
can’t modify it
server – Create many (> 1~2) foreign
methods on a server class or
Date newStart = new Date (previousEnd.getYear(), many other classes need the
previousEnd.getMonth(), same foreign method => use
previousEnd.getDate() + 1); Introduce Local Extension
client • Mechanics
Date newStart = nextDay(previousEnd); – Create a method in the client
class (you need)
private static Date nextDay(Date arg) { • The method should access no
return new Date (arg.getYear(),arg.getMonth(), feature on client class.
arg.getDate() +1); – Make an instance of the server
} class the first parameter
– Comment the method as “foreign
method; should be in server”
Create a method in • Mark the comment as text to easy
client class refactoring again
21. Introduce Local Extension
Q: Need to add • Motivation
methods(>1~2) in server – Can’t modify => group the methods
together & using object-oriented
class, but can’t modify it techniques(subclass/ warp) to do (local
extension)
Date – Local extension
• A separate class & a subtype of extended
class
Client Class • [methods & data should be packaged into
well-formed units]
nextDay(Date):Date
• Mechanics
MfDate – Create an extension class either as a
subclass or a wrapper of the original
nextDay():Date
Create a new class – Add converting constructors to the
Subclass extension
that contains these • Constructor takes the original as an
extra method Class mfDate extends Date argument
{ • Subclass calls an superclass constructor
public nextDay()... • Wrapper sets the delegate field to the
Make this extension public dayOfYear()... argument
class a subclass or – Add new features to the extension
– Replace the original with the extension
wrapper of the where needed
original class mfDate { – Move any foreign methods defined for this
private Date _original; class onto the extension
Wrapper
(delegation)
22. Example for Introduce Local Extension -
subclass
Original code … Refactoring…
class MfDateSub extends Date… class MfDateSub extends Date {
public MfDateSub (String dateString) { public MfDateSub (String dateString) {
super (dateString); super (dateString);
}; };
public MfDateSub (Date arg) {
super (arg.getTime());
Add a converting constructor
}
public MfDateSub (Date arg) { Date nextDay() {
super (arg.getTime()); return new Date (getYear(),getMonth(), getDate() + 1);
} }
}
client class...
Move Method
private static Date nextDay(Date arg) {
// foreign method, should be on date
return new Date (arg.getYear(),arg.getMonth(), arg.getDate() + 1);
}
23. Example for Introduce Local Extension
- wrapper
1. Declare a class 4. Move Method
class mfDate { client class...
private Date _original; private static Date nextDay(Date arg) {
} // foreign method, should be on date
return new Date (arg.getYear(),arg.getMonth(),
2. Set the constructors by a delegation arg.getDate() + 1);
public mfDateWarp (String dateString) { }
_original = new Date (dateString);
}; Put back to class
2.1 Set the instance variable class MfDate...
public mfDateWarp (String arg) {
_original = arg; Date nextDay() {
} return new Date (getYear(),getMonth(),
getDate() + 1);
3. Delegate all methods }
public int getYear() {
return _original.getYear();
}
public boolean equals (MfDateWrap arg) {
return (toDate().equals(arg.toDate()));
}
http://hi.baidu.com/hanframe_ip/blog/item/76c3b154d21aac50d0090659.html
24. Problem: Introduce Local
Extension – wrapper
Override => hide
wrapper info.
Public boolean after (Date arg)
wrapper problem: Can’t alter
aWrapper.after(aDate)
aDate.after(aWrapper)
Public boolean equals (Date arg)
cause problem: public boolean equalsDate (Date arg)
equals is symmetric !!
public boolean equalsDate
(MfDateWrap arg)
25. Summary –
How to put responsibilities ?
• Refactoring – basic idea
– Move Field > Move Method
• Class over responsibilities or less
– Extract Class V.S Inline Class
• Class uses another Class
– Hide Delegate (hide their relationship)
• As Hide delegate causes owner’s interface change
in frequency
– Remove Middle Man
• As can’t modify a class, but want add…
– Introduce Foreign Method (only for 1~2 methods)
– Introduce Local Extension
• Subclass vs. wrapper (delegate field)