1、重構原則:
一、三次法則(事不過三,三則重構)
二、什麼時候重構:
(1)添加功能時重構
(2)修補錯誤時重構
(3)複審代碼時重構
三、重構優勢:
(1)容許邏輯共享
(2)分開解釋意圖和實現
(3)隔離變化
(4)封裝條件邏輯
四、重構難點:
(1)數據庫
(2)修改接口
解決辦法:不要過早發佈接口,請修改你的代碼全部權策略,使重構更順暢。
(3)難以經過重構手法完成的設計改動
五、重構與設計:
重構肩負一項特殊使命,它和設計彼此互補。git
github地址:https://github.com/line007/jucdemo2github
博客地址:https://line007.github.io/算法
2、代碼壞味道
一、Duplicated Code(重複代碼)
二、Long Method(過長函數)
三、Large Class(過大的類)
四、Long Parameters List(過長參數列)
五、Divergent Change(發散式變化)-> 一個類受多種變化的影響
六、Shotgun Surgery(霰彈式修改)-> 一種變化引起多個類相應修改
七、Feature Envy(依戀情結)-> 將數據和數據的操做行爲包裝在一塊兒的技術
八、Data Clumps(數據泥團)
九、Primitive Obsession(基本類型偏執)
十、Switch Statements(switch驚悚現身)
十一、Parallel Inheritance Hierarchies(平行繼承行爲)
十二、Lazy Class(冗餘類)
1三、Sepculative Generality
1四、Temporary Field(使人迷惑的暫時字段)
1五、Message Chain(過分耦合的消息鏈)
1六、Middle Many(中間人)
1七、Inappropriate Intimacy(狎暱關係)
1八、Alternative Classes with Different Interfaces(殊途同歸的類)
1九、Incomplete Library Class(不完善的類庫)
20、Data Class(純稚的數據類)
2一、Refused Bequest(被拒絕的遺贈)
2二、Comments(過多的註釋)-> 當你感受須要撰寫註釋時,請先嚐試重構,試着讓全部註釋變得多餘。數據庫
3、構築測試體系
一、確保全部測試都徹底自動化,讓它們檢查本身的測試結果。
二、一套測試就是一個強大的偵測器,可以大大縮短查詢bug所須要的時間
三、頻繁的運行測試,每次編譯請把測試也考慮進去————天天至少執行每一個測試一次
四、每當你收到bug報告,請先寫一個單元測試來暴露bug.
五、編寫未臻完善的測試並實際運行,好過對完善測試的無盡等待。
六、考慮可能出錯的邊界條件,把測試火力集中在那兒
七、當事情被認爲應該出錯時,別忘了檢查是否拋出了預期的異常。
八、不要由於測試沒法捕捉全部bug就不寫測試,由於測試的確能夠捕捉到大多數的bug。session
4、重構列表
一、重構記錄格式:
name、summary、motivation、mechanics、examples
(1)summary:
一句話,介紹這個重構可以幫助解決的問題
一段簡述,介紹你應該作的事
一幅速寫圖,簡單展示重構先後示例
(2)mechanics:
簡短的筆記,爲了讓本身在一段時間不作某項重構以後還能記錄怎麼作。
(3)examples
像是簡單有趣的教科書
二、尋找引用點
(1)若是被刪除的部分在繼承體系中聲明不上一次
(2)編譯器可能很慢
(3)編譯器沒法找到經過反射機制而獲得的引用點app
5、從新組織函數ide
1、Extract Method(提煉函數) motivation:喜歡簡短而命名良好的函數,細粒度函數,被複用的機會更大,函數的覆寫也更容易些 mechanics: (1)創造一個新函數 (2)將提煉的代碼從源函數複製到新建的目標函數中 (3)仔細檢查提煉出的代碼,看看其中是否引用了「做用域源於源函數」的變量(包括局部變量和源函數參數) (4)檢查是否有「僅用於被提煉代碼段」的臨時變量。若是有,在目標函數中將它們聲明爲臨時變量 (5)檢查被提煉代碼段,看看是否有任何局部變量的值被它改變。 (6)將被提煉代碼段中須要讀取的局部變量,看成參數傳給目標函數 (7)處理完全部局部變量 2、Inline Method(內聯函數) (1)查檢函數,肯定它不具有多態性 (2)找出這個函數的全部被調用點 (3)將這個函數的全部被調用點都替換爲函數本體 (4)編譯,測試 (5)刪除該函數的定義 int getRating() { return moreThanFiveDeliveries() ? 2 : 1; } boolean moreThanFiveDelivers() { return _numberOfLateDeliveries > 5; } -> int getRating() { return (_numberOfLateDeliveries > 5) ? 2 : 1; } 3、Inline Temp(內聯臨時變量) double basePrice = anOrder.basePrice(); return (basePrice > 1000); -> return (anOrder.basePrice() > 1000); 4、Replace Temp With Query(以查詢取代臨時變量) double basePrice = _quantity * _itemPrice; if (basePrice > 1000) return basePrice * 0.95; else return basePrice * 0.98; -> if (basePrice() > 1000) return basePrice() * 0.95; else return basePrice() * 0.98; double basePrice() { return _quantity * _itemPrice; } 示例: double getPrice() { return basePrice() * discountFactor(); } -> double basePrice() { return _quantity * _itemPrice; } double discountFactor() { if (basePrice() > 1000) return 0.95; return 0.98; } 5、Introduce Explaing Variable(引入解釋性變量) if ((platform.toUpperCase().indexOf("MAC") > -1) && (browser.toUpperCase().indexOf("IE") > -1) && wasInitialized() && resize > 0) { // do something } -> final boolen isMacOs = platform.toUpperCase().indexOf("MAC") > -1; final boolean isIEBrowser = browser.toUpperCase().indexOf("IE") > -1; final boolean wasResized = resize > 0; if (isMacOs && isIEBrowser && wasResized) { // do something } double price() { return _quantity * _itemPrice - Math.max(0, _quantity - 500) * _itemPrice * 0.05 + Math.min(_quantity * _itemPrice * 0.1, 100.0); } -> double price() { final double basePrice = _quantity * _itemPrice; final double quantityDiscount = Math.max(0, _quantity - 500) * _itemPrice * 0.05; final double shpping = Math.min(basePrice * 0.1, 100.0); return basePrice - quantityDiscount + shpping; } -> double price() { return basePrice() * quantityDiscount() * shpping(); } double basePrice() { return _quantity * _itemPrice; } double quantityDiscount() { return Math.max(0, _quantity - 500) * _itemPrice * 0.05; } double shpping() { return Math.min(basePrice * 0.1, 100.0); } 6、Split Temporary Variable(分解臨時變量) double temp = 2 * (_heigth + _width); sysout(temp); temp = _heigth * _width; sysout(temp); -> final double perimeter = 2 * (_heigth + _width); sysout(perimeter); final double area = _heigth * _width; sysout(area); -> double getDistanceTravelled(int time) { double result; finall double primaryAcc = _primaryForce / _mass; int primaryTime = Math.min(time, _delay); return = 0.5 * primaryAcc * primaryTime * primaryTime; int secondaryTime = time - _delay; if (secondaryTime > 0) { double primaryVel = primaryAcc * _delay; final double secondaryAcc = (_primaryForce + _secondaryForce) / _mass; result += primaryVel * secondaryTime + 0.5 * secondaryAcc * secondaryTime * secondaryTime; } return result; } ... 7、Remove Assignments to Parameters(移除對參數的賦值) int discount(int inputVal, int quantity, int yearToDate) { if (inputVal > 50) inputVal -= 2; // do something } -> int discount(int inputVal, int quantity, int yearToDate) { int result = inputVal; if (inputVal > 50) result -= 2; // dosomething } -> int discount(int inputVal, int quantity, int yearToDate) { if (inputVal > 50) inputVal -= 2; if (quantity > 100) inputVal -= 1; if (yearToDate > 10000) inputVal -= 4; return inputVal; } -> int discount(final int inputVal, final int quantity, final int yearToDate) { int result = inputVal; if (inputVal > 50) result -= 2; if (quantity > 100) result -= 1; if (yearToDate > 10000) result -= 4; return result; } 8、Replace Method With Method Object(以函數對象取代函數) class Account { int gamma (int inputVal, int quantity, int yearToDate) { int importantValue1 = (inputVal * quantity) + delta(); int importantValue2 = (inputVal * quantity) + 100; if (yearToDate - importantValue1 > 100) importantValue2 -= 20; int importantValue3 = importantValue2 * 7; // do something return importantValue3 - 2 * importantValue1; } } -> Gamma { private final Account _account; private int inputVal; private int quantity; private int yearToDate; private int importantValue1; private int importantValue2; private int importantValue3; Gamma(source, inputVal, quantity, yearToDate) { _account = source; inputVal = inputVal; ... } int compute() { importantValue1 = (inputVal * quantity) + _account.delta(); importantValue2 = (inputVal * quantity) + 100; if (yearToDate - importantValue1 > 100) importantValue2 -= 20; importantValue3 = importantValue2 * 7; // do something return importantValue3 - 2 * importantValue1; } -> importThing() { if (yearToDate - importantValue1 > 100) importantValue2 -= 20; } } -> int gamma (int inputVal, int quantity, int yearToDate) { return new Gamma(this, inputVal, quantity, yearToDate).compute(); } 9、Substitude Algorithm(替換算法) String foundPerson(String[] people) { for(int i=0; i < people.length; i++) { if (people[i].equals("Don")) { return "Don"; } if (people[i].equals("John")) { return "John"; } if (people[i].equals("Kent")) { return "Kent"; } return ""; } } -> String foundPerson(String[] people) { List<String> candidates = Arrays.asList("Don","John","Kent"); for(int i=0; i < people.length; i++) if (candidates.contains(people[i])) return people[i]; return ""; }
6、在對象之間搬移特性函數
1、Move Method(搬移函數) 動機:搬移函數是重構理論的支柱,解耦。 作法: (1)檢查源類中被源函數所使用的一切特性(包括字段和函數),考慮它們是否也該被搬移。 (2)檢查源類的子類和超類,看看是否有該函數的其它聲明。 (3)在目標類中聲明這個函數。 (4)將源函數的代碼複製到目標函數中,並在源函數調用後者。 (5)編譯目標類 (6)決定如何從源函數正確引用目標對象 (7)修改源函數,使之成爲一個純委託函數 (8)編譯,測試 (9)決定是否刪除源函數,或將它看成一個委託函數保留下來。 (10)若是要移除源函數,請將源類中對源函數的全部調用,替換爲目標函數的調用。 class Account { double overdraftCharge() { /**f (_type.isPreminum()) { double result = 10; if (_dayOverdrawn > 7) result += (_dayOverdrawn - 7) * 0.85; return result; } else return _dayOverdrawn * 1.75;*/ return _type.overdraftCharge(_dayOverdrawn); } double bankCharge() { double result = 4.5; // if (_dayOverdrawn > 0) result += overdraftCharge(); if (_dayOverdrawn > 0) result += _type.overdraftCharge(_dayOverdrawn); return result; } } -> class AccountType { double overdraftCharge(int dayOverdrawn) { if (isPreminum()) { double result = 10; if (dayOverdrawn > 7) result += (dayOverdrawn - 7) * 0.85; return result; } else return dayOverdrawn * 1.75; } } 2、Move Field 搬移字段 動機:在類之間移動狀態與行爲,是重構過程當中必不可少的措施。 作法: (1)若是字段的訪問級是pulic,使用Encaspsulate Field將它封裝起來 (2)編譯,測試 (3)在目標類中創建與源字段相同的字段,並同時創建相應的設值/取值函數。 (4)編譯目標類。 (5)決定如何在源對象中引用目標對象 (6)刪除源字段。 (7)編譯,測試 class Account { private AccountType _type; // private double _interestRate; double interestForAmount_days(double amount, int days) { return getInterestRate() * amount * days / 365; } private void setInterestRate(double arg) { _type.setInterestRate(arg); } private double getInterestRate() { return _type.getInterestRate(); } } class AccountType { private double _interestRate; void setInterestRate(double arg) { _interestRate = arg; } double getInterestRate() { return _interestRate; } } 3、Extract Class(提煉類) 動機:單類作一件事情 作法: (1)決定如何分解類所負的責任 (2)創建一個新類,用以表現從舊類中分離出來的責任 (3)創建「從舊類訪問新類」的鏈接關係 (4)對於你想搬移的每個字段,運用Move Field搬移 (5)每次搬移後,從新編譯 (6)將必要函數搬移至新類,從低層函數(被其餘函數調用次數>調用其餘函數)到高層函數 (7)每次搬移後,從新編譯 (8)檢查,精簡每一個類的接口。 (9)決定是否公開新類。 class Person { String _name; TelephoneNumber _officeTelephone; String _officeAreaCode; String _officeNumber; // setter、getter String getTelephoneNumber() { return _officeTelephone.getTelephoneNumber(); } TelephoneNumber getOfficeTelephone() { return _officeTelephone; } } -> class TelephoneNumber { String _areaCode; String _number; // setter、getter String getTelephoneNumber() { return ("(" + _areaCode + ")" + _number); } } 4、Inline Class(將類內聯化) class Person { String _name; TelephoneNumber _officeTelephone; String _officeAreaCode; String _officeNumber; // setter、getter String getTelephoneNumber() { return _officeTelephone.getTelephoneNumber(); } TelephoneNumber getOfficeTelephone() { return _officeTelephone; } class TelephoneNumber { String _areaCode; String _number; // setter、getter String getTelephoneNumber() { return ("(" + _areaCode + ")" + _number); } } } 5、Hide Delegate(隱藏「委託關係」) 動機:"封裝"即便不是對象的最關鍵特徵,也是最關鍵特徵之一。 作法: (1)對於每個委託關係中的函數,在服務對象端創建一個簡單的委託函數。 (2)調整客戶 (3)每次調整後,編譯並測試 (4)若是未來再也不有任何客戶須要取用Delegate,便可移除服務類 (5)編譯、測試 class Person { Department _department; // setter、getter } class Department { String _chargeCode; Person _manager; public Department(Person manager) { _manager = manager; } public Person getManager() { return _manager; } ... } -> manager = john.getDepartment().getManager(); -> manager = john.getManager(); 6、Remove Middle Man(移除中間人) class Person { Department _department; // setter、getter } class Department { String _chargeCode; Person _manager; public Department(Person manager) { _manager = manager; } public Person getManager() { return _manager; } ... } 7、Indroduce Foreign Method(引入外加函數) Date newStart = new Date(previous.getYear(), previousEnd.getMonth(), previousEnd.getDate() + 1); -> private static Date nextDay(Date arg) { return new Date(arg.getYear(), arg.getMonth(), arg.getDate() + 1) } 8、Indroduce Local Extension(引入本地擴展) 動機:很遺憾,類的做者沒法預知將來,他們經常沒能爲你預先準備一些有用的函數。 作法: (1)創建一個擴展類,將它做爲原始類的子類或包裝類。 (2)在擴展中加入轉型構造函數。 (3)在擴展類中加入新特性 (4)根據須要,將原對象替換爲擴展對象 (5)將針對原始類定義的全部外加函數搬移到擴展中 待續。。。