讀書筆記《重構 改善既有代碼的設計》

重構 (refactoring) 在不改變代碼的外在的行爲的前提下 對代碼進行修改最大限度的減小錯誤的概率 本質上, 就是代碼寫好以後 修改它的設計。算法

1,書中開始用一個例子簡單闡釋爲何要重構,以及重構的好處數據庫

- 若是沒有重構,程序就會腐敗變質,程序逐漸失去本身的結構,編程

愈來愈難經過讀源碼理解設計,不良重複的代碼不便於後來的修改和閱讀。

- 重構能夠深刻理解代碼而且幫助找到bug。數組

同時重構能夠減小bug引入的機率,方便往後擴展。提升可理解性,
    下降修改爲本。

- 提升編程的速度,良好的設計師快速開發的根本(阻止代碼腐敗,緩存

提升質量,從而提升開發速度)。
 sum:容易閱讀,全部邏輯都只在惟一的地點指定,新的改動不會影響過去的功能,
 儘量的表達條件邏輯。

----------性能優化

  • 1.2,重構的第一步就是創建一個有效的測試機制,好的測試機制能夠幫助重構
  • 1.3,針對案例中的customer類的函數statement進行分析,這個函數太長,須要分解,拆分,由於簡單的代碼塊容易管理。
    做者將這個statement函數裏面的根據電影的類型返回不一樣的價格的方法,提煉出來,代碼簡潔易懂
    關於函數的名稱的命名,做者總結道,只有寫出人類容易理解的代碼 纔是優秀的代碼。

    函數位置:函數應該放在它使用的數據的所屬對象內,因此做者舉例說明那個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,重構 複用與現實

相關文章
相關標籤/搜索