重構 (refactoring) 在不改變代碼的外在的行爲的前提下 對代碼進行修改最大限度的減小錯誤的概率 本質上, 就是代碼寫好以後 修改它的設計。算法
1,書中開始用一個例子簡單闡釋爲何要重構,以及重構的好處數據庫
- 若是沒有重構,程序就會腐敗變質,程序逐漸失去本身的結構,編程
愈來愈難經過讀源碼理解設計,不良重複的代碼不便於後來的修改和閱讀。
- 重構能夠深刻理解代碼而且幫助找到bug。數組
同時重構能夠減小bug引入的機率,方便往後擴展。提升可理解性, 下降修改爲本。
- 提升編程的速度,良好的設計師快速開發的根本(阻止代碼腐敗,緩存
提升質量,從而提升開發速度)。 sum:容易閱讀,全部邏輯都只在惟一的地點指定,新的改動不會影響過去的功能, 儘量的表達條件邏輯。
----------性能優化
函數位置:函數應該放在它使用的數據的所屬對象內,因此做者舉例說明那個customer中的statement函數根據不一樣類的做用作了拆分,好比獲取電影的價錢和積分數放在租賃類中,獲取總電影價以及總積分放在Customer類中,這樣邏輯清晰。多線程
2,重構原則框架
2.1,何時重構 - 添加新功能是重構 - 修復bug時重構 - 評審代碼是重構(結對編程)
重構小技巧:函數
- 容許邏輯共享 - 分開解釋意圖或實現 - 隔離變化 例如一個類須要在2個地方使用,可使用子類來處理變化 4)封裝條件邏輯 2.2, 重構與設計 前提簡單可行的設計(不必定要徹底正確), 重構能夠預先取代設計,先把功能作好 而後重構 「極限編程」 支持這種觀點,證實這種方法可行 2.3,重構與性能 時間預算 持續關注法(深度理解代碼,而且知道哪些地方耗費時間) 性能優化案例: 大字符串操做耗費性能? 字符串緩存起來 - 改用文件流處理 - 多處理器計算機,多線程 系統從運算1000個小時 到 2個小時的飛速提高
3,代碼壞味道性能
重複代碼 - 同一個類 2個函數有相同的表達式 - 兄弟子類有相同的表達式 - 2個獨立的類有相同的代碼 3.2 函數過長 分解函數的原則:每當感受須要註釋說明點什麼的時候,咱們須要把須要說明的東西寫到一個獨立的函數中,並以其用途來命名。若是代碼前面有一條註釋,就說明這個地方須要提煉出一個函數。 條件表達式(多個if else 改爲三元表達式 Decompose Conditional) 和循環也是提煉的信號, 循環能夠提煉到一個單獨的函數中。 3.3 過大的類 --解決重複代碼的問題 3.4 過長的參數列,建議使用參數對象,傳遞一個參數。 3.5 發散式變化(一個類受到多種變化的影響),散彈式修改 一個變化引起多個類修改 解決常常被修改的類,將常常須要修改的變量字段提煉到一個獨立的類。 3.6 依戀情節 數據和數據操做包裝在一塊兒 原則:將老是一塊兒發生變化的東西放在一塊兒。 3.7 數據泥團 類中有相同的屬性 函數中有相似的參數 刪除一個不影響這個類或者函數的意義,建議拆分新對象 減小字段或者參數的個數 3.8,switch statement 大多狀況下 出現switch語句 就應該考慮多態來替換,這樣的switch須要提煉到一個獨立的函數而且將它放到須要處理的類中。 3.9 平行繼承 目的在於消除類之間的重複代碼。 3.10 冗贅類 無用的代碼類。 3.11 暫時使用的字段 只有執行某個代碼塊纔有意義的字段 抽到一個獨立的類中 3.12 過多的註釋 試圖用函數提煉代碼,刪除註釋
4,構建測試體系
意義:一套測試就是一個強大的bug監聽器 可以大大減小bug查找時間 創建一個獨立的類用於測試而且在框架中運用它,使測試工做更加輕鬆 4.1 Junit 測試框架的運用 每收到一個bug,先寫一個單元測試來暴露bug,考慮可能出錯的邊界條件,把測試的重點集中在那
5 重構列表
6.重構函數
重構的大部分集中在函數的重構,參數的重構 內聯函數 去除間接層 1,不要對參數賦值(Java 1.1 容許將參數定義爲final)。 2,若是臨時變量被屢次賦值,應該分解多個獨立的臨時變量 3,分析臨時變量是否被公用,把這個臨時變量提煉到一個函數中(Replace temp with query) 4,臨時變量用於分解複雜的表達式 5,替換算法,將比較複雜的算法替換成簡單的 易懂的(或者將大的算法分紅小部分 易於理解,而後尋找小算法的替換方案)
7,對象之間的搬移特性
1,函數搬移 -- 一個類函數過多 或者 函數依賴的類比較多 考慮這個函數與哪一個類接觸較多來決定搬移路徑。 2,字段搬移 -- 隨着業務的發展,發現一個類中的一個字段被多個函數或者類共同使用,考慮搬移這個字段或者可見度的改變(將這個字段變成public) 3,提煉類 -- 隨着業務的發展 一個類中有太多的函數和字段 考慮把相似的字段和函數搬移出去。 4,內聯化 -- 將一些不獨立承擔任務的類塞到另一個獨立類中,這種類稱爲萎縮類。 5, 委託關係,簡單理解爲封裝函數,便於往後修改變得容易。
8,從新組織數據
1,自封裝字段 簡單理解就是將這個字段須要轉換的值經過函數的方式表達 getBussinessVal(); 2,以對象取代數據值 一個字段存在多種數據與行爲一塊兒使用纔有意義時 建議將字段變成對象。 3,將值對象(數值)變成引用用對象(物體) 同一個對象被多個其餘的類同時用到,好比 數據庫鏈接,這個時候咱們用工廠模式 進行復用來重構。 4,以對象取代數組, 方便用戶記憶 5,將系統的魔法數定義爲常量 static final double WEIGHT_CONSTANT = 9.18; 6, 封裝字段,將行爲和意義相似的字段封裝起來定義爲private 提供public的訪問函數 7,封裝對象關聯的集合,和數組 根據業務邏輯封裝 list.add 改成emp.addEmp(); 將相似操做對象的集合統一集中到這個類中 8,運用多態繼承取代switch,將涉及到switch的類型碼判斷 統一移動對象類裏面處理 9,子類中不該該有常量 若是有在超類中申明抽象類 在子類實現 返回不一樣值
9,簡化條件表達式
1,分解合併條件表達式 - 利用函數的方法 將多個判斷條件根據業務邏輯封裝成獨立函數,用功能目的來命名,而後調用函數判斷 例如 if (notData(params)){...} else {...} 2,合併上下重複的條件判斷 3,循環邏輯用break 和 return 取代控制指標 好比 while(a<b){a.setA(true)} 這裏用break直接跳出來,便於清晰邏輯判斷 4,使用簡單的if else 來判斷邏輯,若是出現簡單的if else 不能判斷的,用獨立的函數獨立封裝解決
10,簡化函數調用
1,修改函數的名稱 -- 首先考慮給這個函數寫上一句怎樣的註釋,而後想辦法將註釋變成函數的名稱
2,將查詢函數和修改函數分離 若是發現一個函數有返回值又有反作用的函數 優化方法,將查詢到的值緩存到某個字段中,這樣後續的查詢就能夠加快速度
3,將2個相似的函數 經過函數合併 減小代碼量
4,保持函數參數傳遞的對象完整 好比 in a = sumANums(b).getM(); 直接傳遞真個對象 plan.setCountNums(sumANums(b));
5, 儘可能減小參數個數,建議傳遞一個對象做爲參數
6,將一些常常一塊兒傳遞的參數做爲一個參數對象傳遞
7,移除設值函數,發現系統中有一個字段初始化被賦值一次而後一直不變 移除這個字段,而且直接修改這個字段爲final
8,隱藏函數,更加這個函數是否被調用的次數判斷是否設置某些值爲private 和 public,儘量的下降函數的可見度
9,以工廠函數取代構造函數 解決switch的類型碼的判斷
例如:private int _type; static final int A = 1; static final int B = 2; Employee(int type) {_type = type;}static Employee create(int type) { return new Employee(type)}
11, 處理歸納關係
1,字段上移 兩個類擁有相同的字段 將該字段移至超類 2,函數上移,兩個函數,在各個子類中產生徹底相同的結果,將該函數移至超類 3,構造函數上移,在子類的構造函數中調用它 4,函數、字段下移,相反 5,提煉接口,將兩個接口相同的子集提煉到一個獨立的接口中 6,以委託取代繼承 隨着義務的發展 超類中的一些函數你不須要,這時須要新建一個子類委託繼承你須要的函數,而後再繼承你須要的類
12,大型重構 關於繼承的重構
13,重構 複用與現實