KEMBAR78
Refactoring-ch7 moving feature btw objects | PDF
Refactoring – Ch7
Moving Features Btw Objects
             Chen Jing Fung
               2011/5/18


    http://sourcemaking.com/refactoring
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
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
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
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;
   }
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
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;
                     }
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();
              }
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
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();
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;
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
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;
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
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
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();
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
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
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/
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
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)
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);
                    }
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
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)
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)

Refactoring-ch7 moving feature btw objects

  • 1.
    Refactoring – Ch7 MovingFeatures Btw Objects Chen Jing Fung 2011/5/18 http://sourcemaking.com/refactoring
  • 2.
    Outline • Moving FeaturesBetween 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 MoveMethod (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 MoveMethod (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 Movefield 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 Movefield – 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 ExtractClass (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 HideDelegate 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 RemoveMiddle 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 RemoveMiddle 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 IntroduceLocal 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 IntroduceLocal 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)