Refactoring-ch7 moving feature btw objects
Upcoming SlideShare
Loading in...5
×
 

Like this? Share it with your network

Share

Refactoring-ch7 moving feature btw objects

on

  • 1,232 views

decide how to put responsibilities? ...

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).

Statistics

Views

Total Views
1,232
Views on SlideShare
903
Embed Views
329

Actions

Likes
0
Downloads
12
Comments
0

5 Embeds 329

http://fungsiong.blogspot.com 244
http://fungsiong.blogspot.tw 78
http://fungsiong.blogspot.hk 4
http://fungsiong.blogspot.kr 2
http://fungsiong.blogspot.fr 1

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

Refactoring-ch7 moving feature btw objects Presentation Transcript

  • 1. Refactoring – Ch7Moving Features Btw Objects Chen Jing Fung 2011/5/18 http://sourcemaking.com/refactoring
  • 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 TMechanics:• 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 severalclass 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 fieldclass 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 Fieldclass 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 rClient calls delegate class by serverobject Client Person Department Class getManagerServer builds all methods (delegate method) to be used byclient 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 DelegateOriginal code …class Person… client accesses a persons manager => Department _department; get the department first public Department getDepartment() { manager = john.getDepartment().getManager(); return _department; } Refactoring… public void setDepartment(Departmentarg) { 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 persons 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 orDate newStart = new Date (previousEnd.getYear(), many other classes need the previousEnd.getMonth(), same foreign method => usepreviousEnd.getDate() + 1); Introduce Local Extension client • MechanicsDate 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 theMake this extension public dayOfYear()... argumentclass a subclass or – Add new features to the extension – Replace the original with the extensionwrapper of the where neededoriginal 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 - wrapper1. Declare a class 4. Move Methodclass 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 – wrapperOverride => 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)