软件配置管理(四)代码味道与重构
文章目錄
- 重構的概念及意義
- 代碼味道
- 代碼味道分類
- 1.類內味道
- 1.1 可度量的味道-Measured Smells
- 1.1.1 過長函數(shù)-Long Method
- 1.1.2 過大類-Large Class
- 1.1.3 過長參數(shù)列-Long Parameter List
- 1.1.4 過多的注釋-Comments
- 1.2 不必要的復雜性-Unnecessary Complexity
- 1.2.1 夸夸其談的未來性-Speculative Generality
- 1.3 重復-Duplication
- 1.3.1 重復代碼-Duplicated Code
- 1.3.2 異曲同工的類-Alternative Classes with Different Interfaces
- 1.4條件邏輯-Conditional Logic
- 1.4.1 Switch驚悚現(xiàn)身-Switch Statements
- 2.類間味道
- 2.1 數(shù)據(jù)-Data
- 2.1.1 基本類型偏執(zhí)-Primitive Obsession
- 2.1.2 純稚的數(shù)據(jù)類-Data Class
- 2.1.3 數(shù)據(jù)泥團-Data Clumps
- 2.1.4 令人迷惑的暫時字段-Temporary Field
- 2.2 繼承-Inheritance
- 2.2.1 被拒絕的遺贈-Refused Bequest
- 2.2.2 狎昵關系-Inappropriate Intimacy
- 2.2.3 冗贅類-Lazy Class
- 2.3 職責-Responsibility
- 2.3.1 依戀情結-Feature Envy
- 2.3.2 過度耦合的消息鏈-Message Chains
- 2.3.3 中間人-Middle Man
- 2.4 協(xié)調變化-Accommodating Change
- 2.4.1 發(fā)散式變化-Divergent Change
- 2.4.2 霰彈式修改-Shotgun Surgery
- 2.4.3 平行繼承體系-Parallel Inheritance Hierarchies
- 2.5 庫類-Library Classes
- 2.5.1 不完善的程序庫類-Incomplete Library Class
重構的概念及意義
重構是使用一系列重構手法,在不改變軟件可觀察行為的前提下,調整其結構。提高其可理解性,降低修改成本。
重構可以改進軟件設計、使軟件更加容易理解、幫助找到軟件缺陷、提高變成速度。
代碼味道
指程序中存在的一些不良的編程或設計方案。可以作為重構的指示。
代碼味道分類
1.類內味道
1.1 可度量的味道-Measured Smells
1.1.1 過長函數(shù)-Long Method
方法太長。
手段:
- 把函數(shù)變小:提煉函數(shù)
- 函數(shù)內有大量參數(shù)和臨時變量:以查詢代替臨時變量
- 參數(shù)太多:引入?yún)?shù)對象
- 太多臨時變量和注釋:以函數(shù)對象取代函數(shù)
- 條件表達式和循環(huán):分解條件表達式
1.1.2 過大類-Large Class
一個類職責過多,擁有過多的實例變量。
手段:
- 太多實例變量或太多代碼:提煉類、提煉子類
- 確定客戶端如何使用:提煉接口
- 把數(shù)據(jù)和行為移到一個獨立的領域對象,但保留一些重復數(shù)據(jù):復制“被監(jiān)視的數(shù)據(jù)”。
1.1.3 過長參數(shù)列-Long Parameter List
一個方法需要傳遞太多的參數(shù)。
手段:
- 向已有對象發(fā)出一條請求就可以取代一個參數(shù):以函數(shù)取代參數(shù)
- 參數(shù)缺乏合理的對象歸屬:引入?yún)?shù)對象
- 將來自一個對象的參數(shù)收集起來:保持對象完整
1.1.4 過多的注釋-Comments
在非必要時不要寫注釋,優(yōu)先重構代碼以使代碼具有自解釋性。
手段:
- 需要注釋來解釋一塊代碼做了什么:提煉函數(shù)
- 函數(shù)已經(jīng)提煉出來,但仍需注釋:函數(shù)改名
- 需要注釋來說明系統(tǒng)的需求規(guī)格:引入斷言
1.2 不必要的復雜性-Unnecessary Complexity
1.2.1 夸夸其談的未來性-Speculative Generality
當程序中過量的使用設計模式,導致在代碼的閱讀過程中很難找到主要的邏輯走向。放置過量的鉤子或特殊情況來處理一些非必要的事情,可能在代碼的編寫調試過程中加深跟蹤Bug的難度。
手段:
- 某個抽象類沒有太大作用:折疊繼承體系
- 不必要的委托:將類內聯(lián)化
- 函數(shù)的某些參數(shù)未被使用:移除參數(shù)
- 函數(shù)名稱帶有多余的抽象含義:函數(shù)改名
- 無用函數(shù):內聯(lián)函數(shù)、移除函數(shù)
1.3 重復-Duplication
1.3.1 重復代碼-Duplicated Code
在多個地方發(fā)現(xiàn)相似代碼結構,
手段:
- 同一個類含有相同代碼:提煉函數(shù)
- 兩個兄弟類有相同代碼:提煉函數(shù)、函數(shù)上移、塑造模板函數(shù)
- 兩個不相干類有相同代碼:提煉類
1.3.2 異曲同工的類-Alternative Classes with Different Interfaces
不同的類完成相同的任務,但有不同的簽名(主要指方法簽名)。
手段:
- 兩個函數(shù)做同一件事,但有不同簽名:函數(shù)改名
- 將某些行為移入類,直到兩者協(xié)議相同:搬移函數(shù)
- 必須重復而冗贅移入代碼才能實現(xiàn)上述重構:提煉超類
1.4條件邏輯-Conditional Logic
1.4.1 Switch驚悚現(xiàn)身-Switch Statements
Switch語句(包括if-else)通常會導致代碼重復。
手段:
- 與類型碼相關的函數(shù)或類:提煉函數(shù)、搬移函數(shù)、以多態(tài)取代條件表達式、以子類取代類型碼、以狀態(tài)模式或策略模式取代類型碼。
- 只在單一函數(shù)中有一些選擇事件:以明確函數(shù)取代參數(shù)
- 選擇條件之一是null:引入null對象。
2.類間味道
2.1 數(shù)據(jù)-Data
2.1.1 基本類型偏執(zhí)-Primitive Obsession
基本類型被過度使用。
手段:
- 將原本單獨存在的數(shù)據(jù)值替換成對象:以對象代替數(shù)據(jù)值
- 如果想替換的是類型碼,而類型碼不影響行為:以類取代類型碼
- 如果有與類型碼相關的條件表達式:以子類取代類型碼、以狀態(tài)模式、策略模式取代類型碼
- 如果有一組應該總是被放在一起的字段:提煉類
- 如果在參數(shù)列中看到基本類型的數(shù)據(jù):引入?yún)?shù)對象
- 發(fā)現(xiàn)自己正從數(shù)組中挑選數(shù)據(jù):以對象取代數(shù)組
2.1.2 純稚的數(shù)據(jù)類-Data Class
類只擁有成員變量和對應的setter和getter方法。
手段:
- 對于public字段:封裝字段
- 如果一個容器的字段沒有得到恰當?shù)姆庋b:封裝集合
- 對于那些不該被其他類修改的字段:移除設值函數(shù)
- 找出getter和setter被其他類運用的地點:搬移函數(shù)
- 如果無法搬移整個函數(shù):提煉函數(shù)
- 將getter和setter隱藏起來:隱藏函數(shù)
2.1.3 數(shù)據(jù)泥團-Data Clumps
一些數(shù)據(jù)項同時出現(xiàn)在多個地方,如一對內的成員變量、多個方法簽名的參數(shù)等。
手段:
- 兩個類有相同的字段或許多函數(shù)簽名中的參數(shù)相同:提煉類
- 縮短參數(shù)列表、簡化函數(shù)調用:引入?yún)?shù)對象、保持對象完整
2.1.4 令人迷惑的暫時字段-Temporary Field
一個對象中某個變量僅為某些特定場合而設,導致代碼難以理解。
手段:
- 一個對象中某個實例對象僅為某種特定情況而定:提煉類
- 在“變量不合法”的情況下創(chuàng)建一個null對象,從而避免寫出條件式代碼:引入null對象
2.2 繼承-Inheritance
2.2.1 被拒絕的遺贈-Refused Bequest
子類繼承父類的方法和數(shù)據(jù),但僅使用其中一部分。
手段:
- 子類不想或不需要繼承超類(先為子類新建一個兄弟類):提煉子類、函數(shù)下移、字段下移
- 子類復用了超類的行為,卻又不愿意支持超類的接口,拒絕繼承超類的實現(xiàn):以委托代替繼承
2.2.2 狎昵關系-Inappropriate Intimacy
類之間的關系變得過于緊密。
手段:
- 類之間的關系過于緊密:搬移函數(shù)、搬移字段、將雙向關聯(lián)改為單向關聯(lián)
- 如果兩個類存在共同點:提煉類、隱藏“委托關系”
- 從繼承體系中分離子類:以委托取代繼承
2.2.3 冗贅類-Lazy Class
如果一個類不值其身價,它就應該消失。
手段:
- 對于幾乎沒用的組件:將類內聯(lián)化
- 如果某些子類沒有做足夠的工作:折疊繼承體系
2.3 職責-Responsibility
2.3.1 依戀情結-Feature Envy
一個方法對別的類的興趣高于它本身所在的類。
手段:
- 函數(shù)對某個類的興趣高過對自身所處類的興趣:搬移函數(shù)、搬移字段
- 函數(shù)中只有一部分對其他類更感興趣:提煉函數(shù)、搬移函數(shù)
2.3.2 過度耦合的消息鏈-Message Chains
一個對象請求另一個,后者在請求下一個對象,…這就是消息鏈【a.getB().getC().getD()】。采取這種方式,意味客戶代碼將與查找過程中的導航結構緊密耦合,一旦對象間的關系發(fā)生任何變化,客戶端就不得不做出相應修改。
手段:
- 隱藏“委托關系”
2.3.3 中間人-Middle Man
一個類的接口中,一半的方法都委托給了其他類。
手段:
- 過度運用委托:移除中間人
- 如果“不干實事”的函數(shù)只有幾個:內聯(lián)函數(shù)
- 如果中間人還有其他行為,需要對原對象的行為進行擴展:以繼承取代委托
2.4 協(xié)調變化-Accommodating Change
2.4.1 發(fā)散式變化-Divergent Change
一個類擁有多個引起它變化的原因。
手段:
- 找出某特定原因而造成的所有變化:提煉類
2.4.2 霰彈式修改-Shotgun Surgery
進行某種修改時,必須對多個不同的類進行對應的小修改。
手段:
- 把所有需要修改的代碼放進同一個類中:搬移函數(shù)、搬移字段
- 如果眼下沒有合適的類可以安置這些代碼,就創(chuàng)造一個
- 把一系列相關行為放進同一個類中:將類內聯(lián)化
2.4.3 平行繼承體系-Parallel Inheritance Hierarchies
當為某個類增加子類時,不得不為另一個類也增加一個子類。
手段:
- 讓一個繼承體系的實例引用另一個繼承體系的實例:搬移函數(shù)、搬移字段
2.5 庫類-Library Classes
2.5.1 不完善的程序庫類-Incomplete Library Class
手段:
- 如果只想修改庫類的一兩個函數(shù):引入外加函數(shù)
- 如果想添加大量額外行為:引入本地擴展
總結
以上是生活随笔為你收集整理的软件配置管理(四)代码味道与重构的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 软件配置管理(三)软件配置管理核心功能
- 下一篇: 软件配置管理(五)常用重构技巧