《重构:改善既有代码的设计》-学习笔记一(+实战解析)
我不是個偉大的程序員;我只是個有著一些優(yōu)秀習慣的好程序員而己
本人比較直接,不說虛的,直接上干貨。
目錄
????Duplicated Code(重復的代碼)
????Long Method(過長函數(shù))
???? Long Parameter List(過長參數(shù)列)
????Large Class(過大類)
提前總結就是四招:
??? 一、重復的代碼提煉成函數(shù)
??? 二、把過長的函數(shù)變小
??? 三、參數(shù)列太長或變化太頻繁,參數(shù)對象化
??? 四、大招:類的代碼行數(shù)太多,要考慮提煉子類。
?
?第一招 重復的代碼提煉成函數(shù)
??? 第一種情況是:同一個class內的兩個函數(shù)含有相同表達式(expression)。
void printOwing(String _name) {Enumeration e =_orders.elements();double outstanding = 0.0;// print bannerSystem.out.println ("**************************");System.out.println ("***** Customer Owes ******");System.out.println ("**************************");// calculate outstandingwhile (e.hasMoreElements()) {Order each = (Order) e.nextElement();outstanding += each.getAmount();}//print detailsSystem.out.println ("name:" + _name);System.out.println ("amount" + outstanding);}?
實際上這三部分都可以提煉。
優(yōu)化后的結果
void printOwing(String _name) {printBanner();double outstanding = getOutstanding();printDetails(_name,outstanding); }void printBanner() {// print bannerSystem.out.println ("**************************");System.out.println ("***** Customer Owes ******");System.out.println ("**************************");} void printDetails (String _name,double outstanding) {System.out.println ("name:" + _name);System.out.println ("amount" + outstanding); }double getOutstanding() {Enumeration e = _orders.elements();double result = 0.0;while (e.hasMoreElements()) {Order each = (Order) e.nextElement();result = each.getAmount();}return result;}第二種情況:兩個subclasses有相同的表達式,或者是相似的表達式
??? 優(yōu)化的方法是:抽取相同的表達式(屬性和方法),放在父類里,兩個子類再去繼承。
********************************************************************************** ??????????
如果是相似的表達式,好抽出共性的,則用模板函數(shù)設計模式來處理。
這里用到了JAVA的兩個特性,繼承和多態(tài)。
優(yōu)化的思路1
1、在各個subclass 中分解目標函數(shù),把有差異的部分變成入參,封裝成一個模板函數(shù)。
2、把模板函數(shù)放到父類中。
3、子類根據需要輸入不同的入參,得到需要的結果。
*********************************************************************************
如果是相似的表達式,差異的地方不好抽共性,則用模板函數(shù)設計模式來處理。
這里用到了JAVA的兩個特性,繼承和覆寫(overrides)。
優(yōu)化的思路2
1、在各個subclass 中分解目標函數(shù),使分解后的各個函數(shù)要不完全相同,要不完全不同。
2、父類有一個主函數(shù)包含完全相同的函數(shù)和完全不同的函數(shù):相同的函數(shù),抽到父類中,不相同的函數(shù)在父類中定義一個函數(shù)。
3、子類繼承父類,然后覆寫完全不同的函數(shù),再調用主函數(shù)可得到期望的結果。
第二招 把過長的函數(shù)變小
百分之九十九的場合里,要把函數(shù)變小,只需使用Extract Method(第一招)。找到函數(shù)中適合集在一起的部分,將它們提煉出來形成一個新函數(shù)。
如果函數(shù)內有大量的參數(shù)和臨時變量,它們會對你的函數(shù)提煉形成阻礙。這時就要用Replace Temp with Query來消除這些臨時變量
Replace Temp with Query(以查詢取代臨時變量)
?優(yōu)化思路
1、找出只被賦值一次的臨時變量。
2、將該臨時變量聲明為final
3、編譯:這可確保該臨時變量的確只被賦值一次。
4、將臨時變量等號右側部分提煉到一個獨立函數(shù)中;
5、首先將函數(shù)聲明為private。日后你可能會發(fā)現(xiàn)有更多class需要使用 它,彼時你可再放松對它的保護。
6、編譯,測試:確保提煉出來的函數(shù)無任何連帶影響(副作用),結果不變;
7、把臨時變量全替換成獨立出來的函數(shù);
以上,over!
例子:未優(yōu)化代碼
double getPrice() {int basePrice = _quantity * _itemPrice;double discountFactor;if (basePrice > 1000) discountFactor = 0.95;else discountFactor = 0.98;return basePrice * discountFactor;}開始優(yōu)化 1~3步驟
double getPrice() {final int basePrice = _quantity * _itemPrice;final double discountFactor;if (basePrice > 1000) discountFactor = 0.95;else discountFactor = 0.98;return basePrice * discountFactor;}4~6步驟
double getPrice() {final int basePrice = basePrice();final double discountFactor;if (basePrice > 1000) discountFactor = 0.95;else discountFactor = 0.98;return basePrice * discountFactor;}private int basePrice() {return _quantity * _itemPrice;}7步驟
double getPrice() {final double discountFactor;if (basePrice() > 1000) discountFactor = 0.95;else discountFactor = 0.98;return basePrice() * discountFactor;} private int basePrice() {return _quantity * _itemPrice;}搞定basePrice之后,再以類似辦法提煉出一個discountFactor():
double getPrice() {final double discountFactor = discountFactor();return basePrice() * discountFactor;}private double discountFactor() {if (basePrice() > 1000) return 0.95;else return 0.98;}最后的效果
double getPrice() {return basePrice() * discountFactor();}private double discountFactor() {if (basePrice() > 1000) return 0.95;else return 0.98;}private int basePrice() {return _quantity * _itemPrice;}通過以上的優(yōu)化,一個大函數(shù),已經變成了多個小函數(shù),重點是代碼的可讀性提高了,順帶的代碼量變少。
第三招 參數(shù)對象化
當你看到一個函數(shù)的入參有四,五個,甚至更多時,且好幾個函數(shù)都使用這組入參,這時就要用參數(shù)對象化來優(yōu)化代碼。這些函數(shù)可能隸屬同一個class,也可能隸屬不同的classes 。這樣一組參數(shù)就是所謂的Date Clump (數(shù)據泥團)」。這時用一個對象封裝這些參數(shù),再用對象取代它們。
優(yōu)化思路
1、入參有四,五個,甚至更多時,就要著手優(yōu)化;
2、用一個新的class封裝入參,并把這些參數(shù)設置為private嚴格保護起來,寫這些參數(shù)的get方法和set方法。
3、原函數(shù)的入參變成這個新的class對象,函數(shù)里的參數(shù)用class對象對應的屬性替換。
4、編譯測試;
5、將原先的參數(shù)全部去除之后,觀察有無適當函數(shù)可以運用Move Method 搬移到參數(shù)對象之中。
例子:未優(yōu)化的代碼
@Autowiredprivate AddressService addressService;public List inquireAddressListAccount( Integer pageNum ,Integer pageSize,String addressName,String mobile,String zipCode,String consignee){return addressService.inquireAddressList(pageNum,pageSize,addressName,mobile,zipCode,consignee);}?
優(yōu)化
@Autowiredprivate AddressService addressService;public List inquireAddressListAccount( Integer pageNum ,Integer pageSize,InquireAddressListInput output){return addressService.inquireAddressList(pageNum,pageSize,output);}public class InquireAddressListInput(){private String addressName;private String mobile;private String zipCode;private String consignee;public String getConsignee() {return consignee;}public void setConsignee(String consignee) {this.consignee = consignee;}public String getMobile() {return mobile;}public void setMobile(String mobile) {this.mobile = mobile;}public String getZipCode() {return zipCode;}public void setZipCode(String zipCode) {this.zipCode = zipCode;}public String getAddressName() {return addressName;}public void setAddressName(String addressName) {this.addressName = addressName;}}第四招 大招-提煉類和提煉子類
如果想利用單一class做太多事情,其內往往就會出現(xiàn)太多instance變量。一旦如此,Duplicated Code也就接踵而至了。
Extract Class 是Extract Subclass 之外的另一種選擇,兩者之間的抉擇其實就是委托(delegation)和繼承(inheritance)之間的抉擇。
情況一:某個class做了應該由兩個classes做的事。(Extract Class)
優(yōu)化思路1
1、明確每個class所負的責任,該做什么事情;
2、建立一個新class,用以表現(xiàn)從舊class中分離出來的責任;
3、建立「從舊class訪問新class」的連接關系;
4、每次搬移后,編譯、測試。
5、決定是否讓新的class曝光。
例子:未優(yōu)化的代碼
class Person{private String _name;private String _officeAreaCode;private String _officeNumber;public String getName() {return _name;}public String getTelephoneNumber() {return ("(" + _officeAreaCode + ") " + _officeNumber);}String getOfficeAreaCode() {return _officeAreaCode;}void setOfficeAreaCode(String arg) {_officeAreaCode = arg;}String getOfficeNumber() {return _officeNumber;}void setOfficeNumber(String arg) {_officeNumber = arg;}}優(yōu)化1~2步驟
可以將「與電話號碼相關」的行為分離到一個獨立class中
class TelephoneNumber{private String _number;private String _areaCode;public String getTelephoneNumber() {return ("(" + _areaCode + ") " + _number);}String getAreaCode() {return _areaCode;}void setAreaCode(String arg) {_areaCode = arg;}String getNumber() {return _number;}void setNumber(String arg) {_number = arg;}}?
優(yōu)化3步驟
class Person...private String _name;private TelephoneNumber _officeTelephone = new TelephoneNumber();public String getName() {return _name;}public String getTelephoneNumber(){return _officeTelephone.getTelephoneNumber();}TelephoneNumber getOfficeTelephone() {return _officeTelephone;}情況二:class 中的某些特性(features)只被某些(而非全部)實體(instances)用到。Extract Subclass(提煉子類)
優(yōu)化思路2
1、為source class 定義一個新的subclass
2、為這個新的subclass 提供構造函數(shù)。
????簡單的作法是:讓subclass 構造函數(shù)接受與superclass 構造函數(shù)相同的參數(shù),并通過super 調用superclass 構造函數(shù);
3、找出調用superclass 構造函數(shù)的所有地點。如果它們需要的是新建的subclass , 令它們改而調用新構造函數(shù)。
????如果subclass 構造函數(shù)需要的參數(shù)和superclass 構造函數(shù)的參數(shù)不同,可以使用Rename Method 修改其參數(shù)列。如果subclass 構造函數(shù)不需要superclass 構造函數(shù)的某些參數(shù),可以使用Rename Method 將它們去除。
????如果不再需要直接實體化(具現(xiàn)化,instantiated)superclass ,就將它聲明為抽象類。
4、逐一使用Push Down Method 和 Push Down Field 將source class 的特性移到subclass 去。
5、每次下移之后,編譯并測試。
例子:未優(yōu)化代碼
--用來決定當?shù)匦捃噺S的工作報價:
class JobItem ...public JobItem (int unitPrice, int quantity, boolean isLabor, Employee employee) {_unitPrice = unitPrice;_quantity = quantity;_isLabor = isLabor;_employee = employee;}public int getTotalPrice() {return getUnitPrice() * _quantity;}public int getUnitPrice(){return (_isLabor) ?_employee.getRate():_unitPrice;}public int getQuantity(){return _quantity;}public Employee getEmployee() {return _employee;}private int _unitPrice;private int _quantity;private Employee _employee;private boolean _isLabor;class Employee...public Employee (int rate) {_rate = rate;}public int getRate() {return _rate;}private int _rate;?
優(yōu)化1步驟
class LaborItem extends JobItem {}優(yōu)化2步驟
class LaborItem extends JobItem {public LaborItem (int unitPrice, int quantity, boolean isLabor, Employee employee) {super (unitPrice, quantity, isLabor, employee);}}這就足以讓新的subclass 通過編譯了。但是這個構造函數(shù)會造成混淆:某些參數(shù)是LaborItem 所需要的,另一些不是。稍后我再來解決這個問題。
優(yōu)化3步驟
清理構造函數(shù)參數(shù)列
class JobItem...protected JobItem (int unitPrice, int quantity, boolean isLabor, Employee employee) {_unitPrice = unitPrice;_quantity = quantity;_isLabor = isLabor;_employee = employee;}public JobItem (int unitPrice, int quantity) {this (unitPrice, quantity, false, null)}?
外部調用應該使用新構造函數(shù):
? ? ? ?JobItem j2 = new JobItem (10, 15);
測試通過后,再使用Rename Method 修改subclass 構造函數(shù):
class LaborItempublic LaborItem (int quantity, Employee employee) {super (0, quantity, true, employee);}可以將JobItem 的特性向下搬移。先從函數(shù)幵始,我先運用 Push Down Method 對付getEmployee() 函數(shù):
?
class LaborItem extends JobItem {public LaborItem (int unitPrice, int quantity, boolean isLabor, Employee employee) {super (unitPrice, quantity, isLabor, employee);}public LaborItem (int quantity, Employee employee) {super (0, quantity, true, employee);}public Employee getEmployee() {return _employee;}}//因為_employee 值域也將在稍后被下移到LaborItem ,所以我現(xiàn)在先將它聲明為protected。 class JobItem...protected Employee _employee;將_employee 值域聲明protected 之后,我可以再次清理構造函數(shù),讓_employee 只在「即將去達的subclass 中」被初始化:
class JobItem...protected Employee _employee;protected JobItem (int unitPrice, int quantity, boolean isLabor) {_unitPrice = unitPrice;_quantity = quantity;_isLabor = isLabor;}class LaborItem ...public LaborItem (int quantity, Employee employee) {super (0, quantity, true);_employee = employee;}下一個優(yōu)化_isLabor 值域,_isLabor 在JobItem是值為false,在LaborItem值為true。
可以用多態(tài)常量函數(shù)。所謂「多態(tài)常量函數(shù)」會在不同的subclass 實現(xiàn)版本中返回不同的固定值
class JobItem...protected boolean isLabor() {return false;}class LaborItem...protected boolean isLabor() {return true;}就可以擺脫_isLabor 值域了
通過多態(tài)代替條件的方式,重構代碼
class JobItem ...public int getUnitPrice(){return (isLabor()) ?_employee.getRate():_unitPrice;}將它重構為:
class JobItem...public int getUnitPrice(){return _unitPrice;}class LaborItem...public int getUnitPrice(){return _employee.getRate();}使用某項值域的函數(shù)全被下移至subclass 后,我就可以使用 Push Down Field 將值域也下移。
最后的結果就是:
public class JobItem {protected JobItem (int unitPrice, int quantity) {_unitPrice = unitPrice;_quantity = quantity;}public int getTotalPrice() {return getUnitPrice() * _quantity;}public int getUnitPrice(){return _unitPrice;}public int getQuantity(){return _quantity;}private int _unitPrice;private int _quantity; }// public class LaborItem extends JobItem {private Employee _employee;public LaborItem(int quantity, Employee employee) {super(0, quantity);_employee = employee;}public Employee getEmployee() {return _employee;}public int getUnitPrice() {return _employee.getRate();} }//public class Employee {public Employee(int rate) {_rate = rate;}public int getRate() {return _rate;}private int _rate; }一個class如果擁有太多代碼,也適合使用Extract Class和Extract Subclass。
想重構代碼,直接把以上四招看情況用上,更多精彩內容,請等待后續(xù)更新。
作者:小虛竹
歡迎任何形式的轉載,但請務必注明出處。
限于本人水平,如果文章和代碼有表述不當之處,還請不吝賜教。
轉載于:https://www.cnblogs.com/zenghw/p/9079541.html
總結
以上是生活随笔為你收集整理的《重构:改善既有代码的设计》-学习笔记一(+实战解析)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: hdu4035 Maze 【期望dp
- 下一篇: LeetCode——16. 3Sum C