OutlineCh. 7 MovingFeatures Between ObjectsMove Method (搬移函式)Move Field (搬移欄位)Extract Class (提練類別)Inline Class (將類別內聯化)Hide Delegate (隱藏「委託關係」)Remove Middle Man (移除中間人)Introduce Foreign Method (加入外加函式)Introduce Local Extension (引入區域性擴展)SummaryClassification 4/22/20112
Classification 4/22/20114Move Method(搬移函式)動機當一個Class有太多的行為.當一個Class與另個Class有太多合作而型成高度耦合(highly coupled).使系統中的classes更簡單。作法這個函式與哪個物件的交流比較多?檢查source class定義的method所使用的一切features.檢查sub class, super class.搬移source method to target method -> Compile target class.決定如何從source 正確引用target object.修改source method 成為delegating method. -> Compile and Test刪除source method 或將它當作一個delegating method保留下來。將source class中對source method的引用替換為target method.Compile and Test.很難決定 -> 或許移動這個函式與否,並不那麼重要。
5.
Move Method (搬移函式)範例Classification4/22/20115Class Account… double overdraftCharge() { if ( _type.isPremium()) { double result = 10; if (_dayOverdrawn > 7) result += (_daysOverdrawn – 7) * 0.85; return result; }// end if else return _daysOverdrawn * 1.75;} // end overdraftChrgedouble bankCharge () { double result = 4.5; if (_daysOverdrawn > 0) result += overdraftCharge(); return result;} // end bankChargeprivate 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
6.
Move Method (搬移函式)Classification4/22/20116Class AccountType… several account types – diff method double overdraftCharge(intdaysOverdrawn) { if (isPremium()) { double result = 10; if (dayOverdrawn > 7) result += (daysOverdrawn – 7) * 0.85; return result; }// end if else return _daysOverdrawn * 1.75;} // end overdraftChrgeClass Account… double bankCharge () { double result = 4.5; if (_days|Overdrawn |> 0) result += _type.overdraftCharge(_daysOverdrawn); return result;} // end bankCharge
Classification 4/22/20118Move Field(搬移欄位)動機某個class的field,被別的class使用更多次。透過Get/Set間接進行。也移動使用這個field的使用者。(取決於介面是否保持一致)使用Extract Class時,也可能需要搬移field,這時會先搬field.作法Encapsulate Field (Get/Set)Compile and Test.在target class建立同樣的field,並建立Getting/Setting。Compile target class.取得target object (若現有field or method不能做到,建一個field來存。將所有source field的引用,改為存取target field。Compile and Test.
9.
Classification 4/22/20119Move Field(搬移欄位)範例Class Account… private AccoutnType _type; private double _interestRate; double interestForAmount)days (double amount, int days) { return )interestRate * amount * days /365; } // end interestForAmountWould like to move interestRate to the AccountType class because it is used more in that class.
10.
Classification 4/22/201110Move Field(搬移欄位)範例Class AccountType…. private double _interestRate;void setInterestRate (double arg) { _interestRate = arg; } double getInterestRate () { return _interestRate; }……….double interestForAccount_days (double amount, int days) { return _type.getInterestRate() * amount * days/365;} // end interestForAccount_daysMove _interestRate.In the original classreference get and set methods for the field.
Classification 4/22/201120Inline Class(將類別內聯化)Person martin = new Person();martin.getOfficeTelephone().setAreaCode ("781");Person martin = new Person();martin.setAreaCode ("781");
Classification 4/22/201123Hide Delegate(隱藏「委託關係」)作法對每一個委託關係中的函式,在server端建立一個簡單的委託函式(delegating method)。調整客戶,令它只呼叫server提供的函式Compile and Test如果將來不再有任何客戶需要用到Delegate,便可移除server中的存取函式Compile and Test
24.
Classification 4/22/201124Hide Delegate(隱藏「委託關係」)class Person{ Department _department; public Department getDepartment(){ return _department;} public void setDepartment(Department arg){ _department = arg;}}範例manager = john.getDepartment().getManager(); public Person getManager(){ return _department.getManager();}class Department{ private String _chargeCode; private Person _manager; public Department(Person manager){ _manager = manager;} public Person getManager(){ return _manager;}}manager = john.getManager();
Classification 4/22/201126Remove MiddleMan (移除中間人)動機Hide Delegate的代價當客戶要用到delegate的新特性時,你就必須在server端添加一個簡單委託函式。delegate的特性(功能)愈來愈多,就愈來愈痛苦。很難說什麼程度的隱藏才是合適的。隨著系統的變化,改成合適的就好了。重構的意義就在於:你永遠不必說什麼對不起,只要把出問題的地方修補好就行了。作法建立一個函式,用以取代delegate(受托物件)。對於每個委託函式(delegate method),在server中刪除該函式,並將「客戶對該函式的呼叫」替換為「對delegate的呼叫」。Compile and Test
27.
Classification 4/22/201127Remove MiddleMan (移除中間人)範例class Person{ Department _department; public Person getManager(){ return _department.getManager();}}class Department{ private Person _manager; public Department(Person manager){ _manager = manager;}}manager = john.getManager();class Person{ public Department getDepartment(){ return _department;}}manager = john.getDepartment().getManager();
28.
Classification 4/22/201128Introduce ForeignMethod (加入外加函式)你所使用的server class需要一個函式,但你無法修改這個class。在client class中建立一個函式,並以一個server class實體作為第一引數(argument)。Date newStart = new Date (previousEnd.getYear(),previousEnd.getMonth(), previousEnd.getDate() + 1);Date newStart = nextDay(previousEnd);private static Date nextDay(Date arg) { return new Date (arg.getYear(),arg.getMonth(), arg.getDate() + 1);}
29.
Classification 4/22/201129Introduce ForeignMethod (加入外加函式)動機你正在使用一個class,它真的很好,為你提供了你想要的所有服務。當你需要一個新服務,這個class卻無法供應…如果可以修改源碼,你便可以加一個新函式,如果不能,你就得在客戶端編碼,補足你要的函式。如果你需要使用很多次,你就得不斷重初這些程式碼…如果你以外加函式實現一個新功能,那就是一個明確信號:這個函式原本應該在提供服務的class中加以實現。如果一個server class加入了大量的外加函式,你就不應該再使用本項重構,而應該使用Introduce Local Extension。
30.
Classification 4/22/201130Introduce ForeignMethod (加入外加函式)作法在client class中建立一個函式,用來提供你需要的功能。這個函式不應該取用client class的任何特性,如果需要,用參數傳給它。以server class實體作為該函式的第一個參數。將該函式注釋為「外加函式(foreign method),應在server class實現。範例Date newStart = new Date (previousEnd.getYear(),previousEnd.getMonth(), previousEnd.getDate() + 1);Date newStart = nextDay(previousEnd);private static Date nextDay(Date arg) { return new Date (arg.getYear(),arg.getMonth(), arg.getDate() + 1);}
Classification 4/22/201134Introduce LocalExtension (引入區域性擴展)範例(Subclass)class MfDateSub extends Date{ public MfDateSub (String dateString) { super (dateString);} public MfDateSub (Date arg) { super (arg.getTime());} Date nextDay() { return new Date (getYear(),getMonth(), getDate() + 1);}}轉型建構式(converting constructors)
35.
Classification 4/22/201135Introduce LocalExtension (引入區域性擴展)class MfDateWrap{ private Date _original; public MfDateWrap(String dateString){ _original = new Date(dateString);} public MfDateWrap(Date arg){_original = arg;}// 為原始類別的所有函式提供委託函式… public intgetYear() {return _original.getYear();} public intgetMonth() {return _original.getMonth();} public intgetDate() {return _original.getDate();} public boolean equals(MfDateWraparg) {return (toDate().equals(arg.toDate()));} Date nextDay(){ return new Date(getYear(), getMonth(), getDate() + 1);}}範例(Wrapper)如何處理「接受原始類別之實體為參數」的函式?只是執行一個單純的委託動作(delegate)public boolean after (Date arg)aWrapper.after(aDate)aWrapper.after(anotherWrapper)aDate.after(aWrapper)