閱讀《重構》的筆記獻上。算法
重構是在不改變軟件可觀察行爲的前提下改善其內部結構。編程
以微小的步伐修改程序。若是你犯下錯誤,很容易即可發現它。數組
一個方法裏面,不該該有不少的代碼,咱們能夠經過分解後重組。session
好的代碼應該清楚的表達出本身的功能,變量名稱是代碼清晰的關鍵。app
儘可能減小臨時變量,大量參數被傳來傳去,很容易跟丟,可讀性差。dom
提煉出邏輯代碼,以便功能複用。ide
重構(名詞):對軟件內部結構的一種調整,目的是在不改變軟件可觀察行爲的前提下,提升其可理解性,下降其修改爲本。函數
重構(動詞):使用一系列重構首發,在不改變軟件可觀察行爲的前提下,調整其結構。單元測試
重構改進軟件設計測試
重構是軟件更容易理解
重構幫助找到bug
重構提升編程速度
幾乎任何狀況下我都反對專門拔出時間進行重構。在我看來重構原本就不是一件應該特別撥出時間作的事情,重構應該隨時隨地的進行。你不該該爲重構而重構,你之因此重構,是由於你想作別的什麼事,而重構能夠幫助你把那些事作好。
第一次作某件事只管去作,第二次作相似的事情會有反感,第三次再作相似的事,你就應該重構。
事不過三,三則重構。
添加功能時重構
修補錯誤時重構
複審代碼時重構
難以修改的程序
難以閱讀的
邏輯重複的
添加新行爲須要修改已有代碼的
帶複雜條件邏輯的
好的程序
容易閱讀
全部邏輯都只是在惟一地點指定
新的改動不會危及現有行爲
儘量簡單表達條件邏輯
重構是這樣一個過程:它在一個目前可運行的程序上進行,在不改變程序行爲的前提下使其具有上述美好性質,使咱們可以繼續保持高速開發,從而新增程序的價值。
沒法穩定運行直接重寫不用重構
項目以及接近最後期限,不該該重構,雖然重構可以提升生產力,可是你沒有足夠的時間,一般標示你其實早該進行重構了。
Duplicated Code 重複代碼
Long Method 過長函數
Large Class 過大的類
Long Parameter List 過長參數列
Divergent Change 發散式變化
Shotgun Surgery 散彈式修改
Feature Envy 依戀情結 (StrategyVisitor)
Data Clumps 數據泥團
Primitive Obsession 基本類型偏執
Switch Statements switch驚悚現身 (使用多態性替換)
Parallel Inheritance Hierarchies 平行繼承體系
Lazy Class 冗贅類
Speculative Generality 誇誇其談將來性
Temporary Field 使人迷惑的暫時字段
Message Chains 過渡耦合的消息鏈
Middle Man 中間人
Inappropriate Intimacy 狎暱關係
Alternative Classes with Different Interfaces 殊途同歸的類
Incomplete Library Class 不完美的庫類
Data Class 純稚的數據類
Refused Bequest 被拒絕的遺贈
Comments 過多的註釋
當你感受須要撰寫註釋時,請先嚐試重構,試着讓全部註釋變得多餘。
確保全部測試都徹底自動化,讓他們檢查本身的測試結果。
一套測試就是一個強大的bug偵察器,可以大大縮減查找bug所須要的時間。
頻繁地運行測試。每次編譯請把測試也考慮進去—— 天天至少執行每一個測試一次。
每當你收到bug報告,請先寫一個單元測試來暴露這個bug。
編寫未完善的測試並執行,好過對完美測試的無盡等待。
考慮可能出錯的邊界條件,把測試火力集中在那兒。
當事情被認爲應該會出錯時,別忘了檢查是否拋出了預期的異常。
不要由於測試沒法捕捉全部bug就不寫測試,由於測試能夠捕捉到大多數的bug。
重構記錄格式
名稱
概要
描述解決的問題
描述要作的事情
速寫圖展現重構前和重構後的示例
動機
作法
範例
重構的基本技巧—小步前進、頻繁測試
模式和重構之間有着一種與生俱來的關係。模式是你但願到達的目標,重構則是到達之路。
Extract Method 提煉函數
你有一段代碼能夠被組織在一塊兒並獨立出來。將這段代碼放進一個獨立函數中,並讓函數名稱解釋該函數的用途。
Inline Method 內聯函數
一個函數,其本體應該與其名稱一樣清楚易懂。在函數調用點插入函數本體,而後移除該函數。
Inline Temp 內聯臨時變量
你有一個臨時變量,只被一個簡單表達式賦值一次,而它妨礙了其餘重構方法。將全部對該變量的引用動做,替換爲對它賦值的那個表達式自己。
Replace Temp with Query 已查詢取代臨時變量
你的程序以一個臨時變量(temp)保存某一個表達式的運算結果。將這個表達式提煉到一個獨立函數(query查詢式)中。將這個臨時變量的全部被引用點替換爲對新函數的調用。新函數可被其餘函數使用。
Introduce Explaining Variable 引入解釋性變量
你有一個複雜的表達式。將該複雜表達式(或其中一部分)的結果放進一個臨時變量,以此變量名稱來解釋表達式用途。
Split Temporary Variable 分解臨時變量
你的程序有某個臨時變量被賦值超過一次,它既不是循環變量,也不是一個集用臨時變量(collecting temporary variable)。針對每次賦值,創造一個獨立。對應的臨時變量。
Remove Assignments to Parameters 移除對參數的賦值
你的代碼對一個參數進行賦值動做。以一個臨時變量取代該參數的位置。
Replace Method with Method Object 以函數對象取代函數
你有一個大型函數,其中對局部變量的使用,使你沒法採用Extract Method。將這個函數放在一個獨立的對象中,如此一來局部變量就變成了對象內的值域,而後你能夠在同一個對象中將這個大型函數分解爲數個小型函數。
Substitute Algorithm 替換算法
你想要把某個算法替換爲另外一個更清晰的算法。將函數本體替換爲另外一個算法。
Move Method 搬移函數
你的程序中,有個函數與其所駐class以外的另外一個class進行更多交流:調用後者,或被後者調用。在函數最常引用的class中創建一個有着相似行爲的新函數。將舊函數變成一個單純的委託函數,或是將舊函數徹底移除。
Extract Class 提煉類
你的程序中,某個field(值域)被所駐class以外的另外一個class更多地用到。在target class 創建一個new field ,修改source field 的全部用戶,令它們改用new field.
Inline Class 將類內斂化
某個class作了應該由兩個classes作的事情。創建一個新的class,將相關的值域和函數從就class搬移到新class。
Hide Delegate 隱藏委託類
客戶直接調用其server object(服務對象)的delegate class。在sever端(某個class)創建客戶所須要的全部函數,用以隱藏委託關係。
Remove Middle Man 移除中間人
某個class作了過多的簡單委託(simple delegation).讓客戶直接調用delegate(受託類)。
Introduce Foreign Method 引入外加函數
你所使用的server class 須要一個額外函數,但你沒法修改這個class。在client class 中創建一個函數,並以一個server class實體做爲第一引數(argument)。
Introduce Local Extension 引入本地擴展
你所使用的server class 須要一些額外函數,但你沒法修改這個class。創建一個新class,使它包含這些額外函數。讓這個擴展品成爲source class的subclass(子類)或(wrapper)外覆類。
Self Encapsulate Field 自封裝字段
你直接訪問一個值域(field),但與值域直接的耦合關係變得逐漸變得笨拙。爲這個值域創建取值/設值函數,而且只有這些函數來訪問值域。
Replace Data Value with Object 以對象取代數據值
你有一筆數據項(data item),須要額外的數據和行爲。將這筆數據項變成一個對象。
Change Value to Reference 將值對象改爲引用對象
你有一個class,衍生出許多相等視圖(equal instance),你但願將它們替換爲單元對象。將這個 value object(實值對象)變成一個reference object(引用對象)。
Change Reference to Value 將引用對象改爲值對象 (equals hashCode)
你有一個reference object(引用對象),很小且不可變,並且不易管理。將他變成一個value object(實值對象)。
Replace Array with Object 以對象取代數組
你有一個數組(array),其中的元素個各自表明不一樣的東西,以對象替換數組。對於數組中的每一個元素,以一個值域表示之。
Duplicate Observed Data 複製「被監聽數據」
你有一些domain data 置身GUI控件中,而domain method 須要訪問之。即那個該筆數據拷貝到以到domain object。創建一個observer模式,用以對domain object和GUI object 內的重複數據進行同步控制(sync).
Change Unidirectional Association to Bidirectional 將單向關聯改成雙向關聯
兩個classes都須要使用對方特性,但其間只有一條單向鏈接。添加一個反向指針,並使修改函數可以同時更新兩條鏈接。
Change Bidirectional Association to Unidirectional 將雙向關聯改爲單向關聯
兩個classes之間有雙向關聯,但其中一個class現在再也不須要另外一個class的特性。去除沒必要要的關聯(association)。
Replace Magic Number with Symbolic Constant 以字面常量取代魔法數
你有一個字面值,帶有特別的含義。創造一個常量,根據其意義爲它命名,並將上述的字面數值替換爲這個常量。
Encapsulate Field 封裝字段
你的class存在一個public值域。將它聲明爲private,並提供相應的訪問函數。
Encapsulate Collection 封裝集合
有個函數返回一個羣集(collection)。放這個函數返回該羣集的一個只讀映件,並在這個class中提供添加移除羣集元素的函數。
Replace Record with Data Class 以數據類取代記錄
你須要面對傳統編程環境中的record structure(記錄結構)。爲該record(記錄)建立一個啞數據對象(dumb data object)。
Replace Type Code with Class 以類取代類型碼
class之中有一個數值別碼,但他並不影響class的行爲。以一個新的class替換數值型別碼。
Replace Type Code with Subclasses 以子類取代類型碼 (多態機制)
你有一個不可變的type code,它會影響class的行爲。以一個subclass取代這個type code。
Replace Type Code with State/Strategy 以State/Strategy取代類型碼
你有一個type code,它會影響class 的行爲,但你沒法使用subclassing。以state object取代type code.
Replace Subclass with Fields 以字段取代子類
你的各個subclasses的惟一差異只在返回常量數據的函數身上。修改這些函數,使他們返回superclass中的某個(新增)值域,而後銷燬sublcasses。
簡化條件表達式
Decompose Conditional 分解條件表達式
你有一個複雜的條件語句。從if、then、else三個段落中分別提出獨立函數。
Consolidate Conditional Expression 合併條件表達式
你有一系列條件測試,都獲得相同結果。將這些測試合併爲一個條件式,並將這個條件式提煉成爲一個獨立函數。
Consolidate Duplicate Conditional Fragments 合併重複的條件片斷
在條件式的每個分支上着相同的代碼。將這個段重複代碼搬移到條件式以外。
Remove Control Flag 移除控制標記 (break/continue/return)
在一系列布爾表達式中,某個變量帶有控制標記的做用,以break語句或return語句取代控制標記。
Replace Nested Conditional with Guard Clauses 以衛語句取代嵌套條件表達式 (單獨判斷被稱爲「衛語句」)
函數中的條件邏輯令人難以看清正常的執行路徑。使用衛語句表現全部特殊的狀況。衛語句就是把複雜的條件表達式拆分紅多個條件表達式,好比一個很複雜的表達式,嵌套了好幾層的if - then-else語句,轉換爲多個if語句,實現它的邏輯,這多條的if語句就是衛語句
Replace Conditional with Polymorphism 以多態取代條件表達式
你手上有個條件式,它根據對象型別的不一樣而選擇不一樣的行爲。將這個條件式的每一個分支放進一個subclass內的覆寫函數中,而後將原始函數聲明爲抽象函數。
Introduce Null Object 引入Null對象
你須要再三檢查某個是否爲null value。將null value(無效值)替換爲null object(無效物)
Introduce Assertion 引入斷言
某一段代碼須要對程序狀態作出某種假設。以assertion(斷言)明確表現這種假設。
Rename Method 函數更名
函數的名稱未能揭示函數的用途,修改函數名稱。
Add Parameter 添加參數
某個函數須要從調用端獲得更多信息。爲此函數添加一個對象參數,讓該對象帶進函數所需信息。
Remove Parameter 移除參數
函數本體再也不須要某個參數,將該參數去除。
Separate Query from Modifier 講查詢函數和修改函數分離
某個函數即返回函數對象狀態值,又修改對象狀態。未來兩個不一樣的函數,其中一個負責查詢,另外一個負責修改。
Parameterize Method 令函數攜帶參數
若干函數作了相似的工做,但在函數本體中卻包含了不一樣的值。創建單一函數,以參數表達那些不一樣的值。
Replace Parameter with Explicit Methods 以明確函數取代參數
你有一個函數,其內徹底取決於參數值而採起不一樣反應。針對該參數的每個可能值,創建一個獨立函數。
Preserve Whole Object 保持對象完整
你從某個對象中取出某個值,將它們做爲某一次函數調用的參數。該引用(傳遞)整個對象。
Replace Parameter with Methods 以函數取代參數
對象調用某個函數,並將所得結果做爲參數,傳遞給另外一個函數。而接受該參數的函數也能夠調用前一個函數。讓參數接受者去除該項參數,並直接調用前一個函數。
Introduce Parameter Object 引入參數對象
某個參數老是很天然地同時出現。以一個對象取代這些參數。
Remove Setting Method 移除設置函數
你的class中的某個值域,應該在對象初創時被設值,而後就再也不改變。去掉該值域的全部設置函數。
Hide Method 隱藏函數
有一個函數,歷來沒有被其餘任何class用到。將這個函數改成private。
Replace Constructor with Factory Method 以工廠函數取代構造函數
你但願在建立對象時不只僅是對它作簡單的構建工做,將construcotr(構造函數)替換爲factory method(工廠函數)
Encapsulate Downcast 封裝向下轉型
某個函數返回的對象,須要由函數調用者執行向下轉型動做。將向下轉型動做移到函數中。
Replace Error Code with Exception 以異常取代錯誤碼
某個函數返回一個特定的代碼,用以表示某種錯誤狀況。改用異常。
Replace Exception with Test 以測試取代異常
面對一個調用者可預先加以檢查的條件,你拋出一個異常。修改調用者,使它在調用函數以前先作檢查。
Pull Up Field 字段上移
兩個subclass擁有相同的值域。將此一值域移至superclass。
Pull Up Method 函數上移
有些函數,在各個subclass中產生徹底相同的效果。將該函數移至superclass。
Pull Up Constructor Body 構造函數本體上移
你的各個subclass中擁有一些構造函數,它們的本體幾乎徹底一致。在superclass中新建一個構造函數,並在subclass構造函數中調用它。
Push Down Method 函數下移
superclass中的某個函數只與部分(而非所有)subclass有關。將這個函數移到相關的那些subclasses去。
Push Down Field 字段下移
superclass中的某個值域只被部分subclass用到。將這個值域到須要它的那些subclasses去。
Extract Subclass 提煉子類
class中某些特性只被某些而非所有實體用到。新建一個subclass,將上面所說的那一部分特性移到subclass中。
Extract Superclass 提煉超類
兩個classes有類似特性。爲這連個classes創建一個superclass.將相同特性移至superclass.
Extract Interface 提煉接口
若干客戶使用class接口中的同一子集。或者,兩個Classes的接口有部分相同。將相同的子集提煉到一個獨立接口中。
Collapse Hierarchy 摺疊繼承體系
superclass和subclass之間無太大區別。將它們和爲一體。
Form Template Method 塑造模板函數
有一些subclasses,其中相應的某些函數以相同順序執行相似的措施,但各措施實際上有所不一樣。將各個措施分別放進獨立函數中,並保持它們都有相同的簽名式,因而原函數也就變得相同了。而後將原函數上移至superclass。
Replace Inheritance with Delegation 以委託取代繼承
某個subclass只使用superclass接口中的一部分,或是更本不須要繼承而來的數據。在subclass中新建一個值域用以保存superclass;調整subclass函數,令它改而委託superclass;而後去掉二者之間的繼承關係。
Replace Delegation with Inheritance 以繼承取代委託
你的兩個classes之間使用了委託關係,並常常爲整個接口編寫許多極其簡單的請託函數。讓請託Class繼承受託class。
Tease Apart Inheritance 梳理並分解繼承體系
某個繼承體系同時承擔兩項責任。創建兩個繼承體系,並經過委託關係讓其中一個能夠調用另外一個。
Convert Procedural Design to Objects 將過程化設計轉化爲對象設計
你手上有一些代碼,以傳統的過程化風格寫就。將數據記錄變成對象,將行爲分開,並將行爲移入相關對象中。
Separate Domain from Presentation 將領域和表述/顯示分離
某些GUI classes之中包含了domain login(領域邏輯)。將domain loginc(領域邏輯)分離出來,爲它們創建獨立的domain classes.
Extract Hierarchy 提煉繼承體系
你有某個class作了太多工做,其中一個部分工做以大量條件式完成的。創建繼承體系,以一個subclass表示一種特殊狀況。