《重構_改善既有代碼的設計》摘要(上)

重構是在不改變軟件可觀察性的前提下,改善軟件的內部結構。程序員

第一章 重構,第一個案例
若是對一段代碼添加一個新特性很麻煩,那就先重構這段代碼,讓添加新特性變得很容易,而後再添加新特性。
重構以前必須有一套可靠的測試機制,這些測試要麼OK,要麼即能列出失敗清單,即具備自我檢驗能力。
重構技術就是以微小的步伐修改程序,並測試經過。
任何一個傻瓜均可以寫出計算機理解的代碼,但只有寫出人類容易理解的代碼,纔是優秀的程序員。算法

第二章 重構原則
一、重構做名詞,是一種對軟件的調整;做動詞,是一種改善軟件結果的方法。
二、兩頂帽子:添加新功能,重構,同一時間只能戴其中一個。添加新功能時,只要保證能經過測試,不重構;重構時再也不添加新功能,只改變程序結構,並讓其經過測試。
三、重構能夠而且應該用於如下目的:
改進軟件設計:若是沒有重構,隨着功能的添加,程序會逐漸腐敗,失去本身的結構,也越來越理解程序的設計。重構就像整理代碼,使設計變得清晰。
使軟件更容易理解:重構的過程至關於理解了軟件的設計結構,這種理解是針對任何維護這個程序的人。另外一方面,能夠用重構來幫助理解不熟悉的代碼。
幫助找到bug:重構時能夠深刻理解程序的行爲,提升了發現bug的機會。
提升編程速度:良好的設計是快速開發的根本,重構正是一種產生良好設計的代碼的手段。
四、什麼時候重構:
三次原則 —— 事不過三,過則重構。
添加新功能時重構,這是最好的時機。
修補錯誤的時候重構
複審代碼時重構,這在開發團隊中有利於知識的傳播和積累。結對編程、極限編程。
五、怎麼對領導說爲何重構:若是領導懂,那說不說無所謂;若是不懂,那就不說。
六、重構的難題:
關於接口的重構:保留原有接口,增長新接口。
難以經過重構的手法完成的設計改動:重構方案進行對比,若是有優點,那就使用它。
什麼時候不應重構:重構的代價大於重寫。項目接近於交付日期。
重構與設計:重構不能取代設計,設計也不能取代重構。設計的目的在於找到一個合適的初始解決方案,沒有完美的設計,能夠經過重構改善設計。
      重構能夠帶來簡單的設計,同時又不下降靈活性,這也下降了設計的難度。
重構與性能:重構可能會下降系統性能,但能夠改善對軟件的理解。若是系統性能的下降能夠接收,那麼就重構吧。
      關於性能,可能大部分的時間消耗在不多的一些代碼上,重點關注這些代碼。在性能優化階段,找出瓶頸點,經過不停地重構、測試、性能度量來進行優化。編程

第三章 代碼的壞味道
味道的好壞要靠直覺來判斷,沒有理論上的定義,說哪一個代碼是好的,哪一個是壞的。
下面是常見的一些壞味道的代碼:
一、Duplicated Code 重複代碼:提取成公共函數,在須要使用的地方調用。
二、Long Method 過長函數:分解該函數。若是說須要一段註釋來解釋一些代碼的做用,那就將這些代碼提煉到一個函數,並以其用途命令。這樣比寫註釋好多了。
還有條件表達式和循環語句。
三、Large Class 過大的類
四、Long Parameter List 過長參數列,包括函數入參和函數內的臨時變量
五、Divergent Change 發散式變化:對於模塊來講,針對外部不一樣的變化類型,內部針對每種變化類型的修改應該越少越好。即一個類受多種變化的影響。
六、Shotgun Surgery 散彈式修改:對來模塊來講,針對一種外部的變化,內部涉及的修改的地方應該越少越好。即一個變化影響多個類。
七、Featura Envy 依戀情結
八、Data Clumps 數據泥團:好比類中的相同字段,函數的相同參數
九、Primitive Obsession 基本類型偏執
十、Switch Satatments switch驚悚現身:能夠用多態
十一、Parallel Inheritance Hierarchies 平行繼承體系
十二、Lazy Class 冗贅類
1三、Speculative Generality 誇誇其談將來性
1四、Temporary Field 使人迷惑的暫時字段
1五、Message Chains 過多耦合的消息鏈
1六、Middle Man 中間人
1七、Inappropriate Intimacy 狎暱關係
1八、Alternative Classes with Different Interfaces 殊途同歸的類
1九、Incomplete Library Class 不完美的庫類
20、Data Class 純稚的數據類
2一、Refused Bequest 被拒絕的遺贈
2二、Comments 過多的註釋:當你須要寫註釋時,請嘗試重構,讓註釋變得多餘。設計模式

第四章 構築測試體系
測試要是自動化的,讓測試本身檢查測試結果。這裏所說的測試都是單元測試。
功能測試每次發現一個新的bug時,寫一個單元測試來暴露這個bug。
測試你把握不大代碼,和各類邊界條件。
測試錯誤的時候,檢查是不是預期的錯誤。
不要認爲測試沒法發現全部bug就不寫測試,但它能發現大多數bug數組

第五章 重構列表
重構的基本技巧:小步前進,頻繁測試。
重構與設計模式之間的關係與生俱來,模式是目標,重構是手段。性能優化

第六章 從新組織函數
一、Extract Method 提煉函數:將一段代碼單獨提取出來,並以用途命名。重點在於對局部變量的優化,包括傳進源函數的參數和源函數內的臨時變量。
二、Inline Method 內聯函數:若是函數本體和函數名同樣清晰,且本體很簡短,那麼就用本體替代函數。
三、Inline Temp 內聯臨時變量:若是一個變量只被使用一次,那麼嘗試內聯它。
四、Replace Temp with Query 以查詢代替臨時變量:若是一個臨時變量只保存某一表達式的結果,將表達式提取成函數,將對臨時變量的引導轉換成對函數的調用。
五、Introduce Explaining Variable 引入解釋性變量:對於一個複雜的表達式,將表達式或其中的一部分的結果放進一個臨時變量,並以變量名來解釋表達式的用途。
六、Split Temporary Variable 分解臨時變量:某個臨時變量被賦值屢次,它既不是循環變量,也不用於收集計算結果,那麼針對每次賦值,建立一個新的臨時變量。
    即讓臨時變量只承擔一次責任。
七、Remove Assignment to Parameters 移除對參數的賦值:代碼對一個參數進行賦值,能夠用一個臨時變量取代參數。
八、Replace Method with Method Object 以函數對象取代函數:有一個大型函數,沒法對局部對象使用Extract Method,那麼將這個函數放進一個對象內,如此依賴局部變量
    就變成對象內的字段。而後在對象內將函數分解成小函數。
九、Substitute Algorithm 替代算法:想要把某個算法替換成更清晰的算法,那麼將函數本體替換成算法。session

第七章 在對象之間搬移特性
一、Move Method 搬移函數:一個函數與其所駐類以外的另外一個類進行更多的交流(調用後者,或者被後者調用),那麼在該函數最常引用的類中
    創建一個有着相似行爲的新函數。將舊函數變成一個單純的委託函數,或者徹底移除。
二、Move Field 搬移字段:某個字段被其所駐類以外的另外一個更多地用到。在目標類新建一個字段,修改源字段的全部用戶,令它們改用新字段。
三、Extract Class 提煉類:某個類作了應該由兩個類作的事。創建一個新類,將相關的字段和函數從舊類搬移到新類。
四、Inline Class 內聯類:某個類沒有作太多事情。將這個類的全部特性搬移到另外一個類中,而後移除原類。
五、Hide Delegate 隱藏委託關係:客戶經過一個委託類來調用另外一個對象。在服務類上創建客戶所需的全部函數,用以隱藏委託關係。
六、Remove Middle Man 移除中間人:某個類作了過多的簡單委託動做。讓客戶直接調用受託類。
七、Introduce Foreign Method 引入外加函數:須要爲提供服務的類增長一個函數,但沒法修改這個類。在客戶類中創建一個函數,並以第一參數
    形式傳入一個服務類實例。
八、Introduce Local Extension 引入本地擴展:須要爲服務類提供一些額外函數,但沒法修改這個類。創建一個新類,使它包含這些額外函數。
    讓這個擴展品成爲源類的子類或包裝類。app

第八章 從新組織數據
一、Self Encapsulate Field 自封裝字段:你直接訪問一個字段,但與字段之間的耦合關係逐漸變得笨拙。爲這個字段創建取值/設置函數,
    並以這些函數來訪問這個字段。
二、Replace Data Value with Object 以對象取代數據值:你有一個數據項,須要與其餘數據和行爲一塊兒使用纔有意義。將數據項變成對象。
三、Change Value to Reference 將值對象改成引用對象:你從一個類衍生出許多彼此相等的實例,但願將它們替換爲同一個對象。
    將這個值對象變成引用對象。
四、Change Reference to Value 將引用對象改爲值對象:你有一個引用對象,很小且不可變,並且不易管理。將它變成一個值對象。
    值對象有一個很是重要的特性:它們應該是不可變的。
五、Replace Array with Object 以對象取代數組:你有一個數組,其中的元素各自表明不一樣的東西。以對象替代數據。
    對於數組中的每一個元素,以一個字段來表示。
六、Duplicate Observed Data 複製被監視數據:你有一些領域數據置身於GUI控件中,而領域函數須要訪問這些數據。
    將該數據複製到一個領域對象中。創建一個Observer模式,用以同步領域對象和GUI對象內的重複數據。
七、Change Unidirectional Association to Bidirectional 將單向關聯改爲雙向關聯:兩個類都須要使用對方特性,但其間只有一條單向鏈接。
    添加一個反向指針,並使修改函數可以同時更新兩條鏈接。
八、Change Bidirectional Association Unidirectional 將雙向關聯改爲單向關聯:兩個類之間有雙向關聯,但其中一個類現在再也不須要另外一個類的特性。
    去除沒必要要的關聯。
九、Replace Magic Number with Symbolic Constant 以字面常量取代魔法數:你有一個字面數值,帶有特別含義。
    創造一個常量,根據其意義爲它命名,並將上述的字面數值替換爲這個常量。
十、Encapsulate Field 封裝字段:你的類中存在一個public字段。將它聲明爲private,並提供相應的訪問函數。
十一、Encapsulate Collection 封裝集合:有一個函數返回一個集合。讓這個函數返回該集合的一個副本,並在這個類中提供添加/移除集合元素的函數。
十二、Replace Record with Data Class 以數據類取代記錄:你須要面對傳統編程環境中的記錄結構。爲該記錄建立一個啞數據對象。
1三、Replace Type Code with Class 以類取代類型碼:類之中有一個數值類型碼,但它並不影響類的行爲。以一個新類替換該數值類型碼。
1四、Replace Type Code with Subclasses 以子類取代類型碼:你有一個不可變的類型碼,它會影響類的行爲。以子類取代這個類型碼。
1五、Replace Type Code with State/Strategy 以State/Strategy取代類型碼:你有一個類型碼,它會影響類的行爲,但你沒法經過繼承手法消除它。
    以狀態對象取代類型碼。
1六、Replace Subclasswith Fields 以字段取代子類:你的各個子類的惟一差異只在」返回常量數據「的函數身上。
    修改這些函數,使它們返回超類中的某個(新增)字段,而後銷燬子類。ide

第九章 簡化條件表達式
一、Decompose Conditional 分解條件表達式:你有一個複雜的條件語句。從if、then、else三個段落中分別提煉出獨立函數。
二、Consolidate Conditional Expression 合併條件表達式:你有一系列條件測試,都獲得相同結果。
    將這些測試合併爲一個條件表達式,並將這個條件表達式提煉成一個獨立函數。若是這些條件表達式之間彼此獨立,那麼慎用本項重構。
三、Consolidate Duplicate Conditional Fragments 合併重複的條件片斷:在條件表達式的每一個分支上有着相同的一段代碼。
    將這段代碼搬移到條件表達式以外。
四、Remove Control Flag 移除控制標記:在一系列布爾表達式中,某個變量帶有控制標記的做用。以break或return取代控制標記。
五、Replace Nested Conditional with Guard Clauses 以衛語句取代嵌套條件表達式:函數中的條件邏輯令人難以看清正常的執行路徑。
    使用衛語句表現全部特殊狀況。精髓就是給某條分支以特別的重視,來表達該條件十分罕見,應該單獨檢查。
    每一個函數一個入口一個出口:入口最好是一個,出口則不必定要是一個,保持代碼清晰纔是關鍵。
六、Replace Conditional with Polymorphism 以多態取代條件表達式:你手上有個條件表達式,它根據對象類型的不一樣而選擇不一樣的行爲。
    將這個條件表達式的每一個分支放進一個子類內的複寫函數中,而後將原始函數聲明爲抽象函數。
七、Introduce Null Object 引入Null對象:你須要再三檢查某對象是否爲Null。將Null值替換爲Null對象。
八、Introduce Assertion 引入斷言:某一段代碼須要對程序狀態作出某種假設。以斷言明確表現這種假設。
    斷言的價值在於:幫助程序員理解代碼正確運行的必要條件。不能濫用斷言,不要使用它來檢查「你認爲應該爲真」的條件,請只使用
    它來檢查「必定必須爲真」的條件。函數

相關文章
相關標籤/搜索