ref: 重構(gòu):改善既有代碼的設(shè)計
Refactoring: Improving the Design of Existing Code
Ch 1 重構(gòu),第一個案例
重構(gòu)的第一個步驟永遠相同:我得為即將修改的代碼建立一組可靠的測試環(huán)境。
好的測試是重構(gòu)的根本。
Ch 2 重構(gòu)原則
重構(gòu):對于軟件內(nèi)部結(jié)構(gòu)的一種調(diào)整,目的是在不改變軟件可觀察行為的前提下,提高其可理解性,降低其修改成本。
Ch 3 代碼的壞味道
3.1 Duplicated Code 重復代碼
3.2 Long Method 過程函數(shù)
更加積極的分解函數(shù)。
原則:每當感覺需要以注釋來說明點什么的時候,我們就把需要說明 東西寫進一個獨立的函數(shù),并以其用途命名。
函數(shù)名命名關(guān)鍵:在于函數(shù)“做什么”和“如何做”之間的語義距離。
3.3 Large Class 過大的類
3.4 Long Parameter List 過長的參數(shù)列
3.5 Divergent Change 發(fā)散式變化
3.6 Shotgun Surgery 散彈式修改
3.7 Feature Envy 依戀情節(jié)
3.8 Data Clumps 數(shù)據(jù)泥團
3.9 Primitive Obsession 基本類型偏執(zhí)
3.10 Switch Statements Switch 語句
3.11 Parallel Inheritance Hierarchy 平行繼承體系
3.12 Lazy Class 冗余類
3.13 Speculative Generality 夸夸其談未來性
3.14 Temporary Field 令人迷惑的暫時字段
3.15 Message Chains 過度耦合的消息鏈
3.16 Middle Man 中間人
3.17 Inappropriate Intimacy 狎昵關(guān)系
3.18 Alternative Classes with Different Intefaces 異曲同工的類
- 19 Imcomplete Library Class 不完美的庫類
3.20 Data Class 數(shù)據(jù)類
3.21 Befused Bequest 被拒絕的遺贈
3.22 Comments 過多的注釋
但你感覺需要撰寫注釋時,請先嘗試重構(gòu),試著讓所有注釋夠變得多余。
Ch 4 構(gòu)筑測試體系
每當你收到bug報告,前先寫一個單元測試來暴露這個bug。
Ch 5 重構(gòu)列表
Ch 6 重新組織函數(shù)
6.1 Extract Method 提煉函數(shù)
6.2 Inline Method 內(nèi)聯(lián)函數(shù)
一個函數(shù)的本體與名稱同樣清楚易懂。
6.3 Inline Temp 內(nèi)聯(lián)臨時變量
6.4 Replace Temp with Query 以查詢?nèi)〈R時變量
你的程序以一個臨時變量保存某一表達式的運算結(jié)果。
將表達式提煉到一個獨立的函數(shù)里。
Example:
double getPrices() {
list basePrice = quantity * itemPrice;
double discountFactor;
if (basePrice > 1000)
discountFactor = 0.95;
else
discountFactor = 0.98;
return basePrice * discountFactor;
}
After refactoring:
double getPrice() {
return basePrice() * discountFactor();
}
private double discountFactor {
if (basePrice() > 1000)
return 0.95;
else
return 0.98;
}
private int basePrice() {
return this.quantity * this.itemPrice;
}
6.5 Introduce Explaining Variable 引入解釋性變量
你有一個復雜的表達式
將該復雜表達式(或其中一部分)的結(jié)果放進一個臨時變量,以此變量名稱來解釋表達式的用途
6.6 Split Temporary Variables 分解臨時變量
你的程序有某個臨時變量被賦值超過一次,它既不是循環(huán)變量,也不被用與收集計算結(jié)果。
針對每次賦值,創(chuàng)建一個獨立、對應(yīng)的臨時變量。
同一個臨時變量承擔兩件不同的事情,會令代碼閱讀者糊涂。
6.7 Remove Assignments to Parameters 移除對參數(shù)的賦值
代碼對一個參數(shù)進行賦值
以一個臨時變量取代該參數(shù)的位置。
6.8 Replace Method with Method Object 以函數(shù)對象取代函數(shù)
你有一個大型函數(shù),其中對局部對象的使用是你無法采用Extract Method
將這個函數(shù)放進一個單獨對象中,如此一來局部變量就成了對象內(nèi)的字段。然后你可以在同一個對象中將這個大型函數(shù)分解為多個小型函數(shù),
6.9 Substitute Algorithm 替換算法
你想要把某個算法替換為另一個更清晰的算法。
將函數(shù)本體替換為另一個算法。
Ch 7 在對象之間搬移特性
7.1 Move Method 搬移函數(shù)
你的程序中,有個函數(shù)與其所在的類之外的另一個類進行更多交流:調(diào)用后者,或被后者調(diào)用。
在該函數(shù)最常引用的類中建立一個有著類似行為的新函數(shù)。將舊函數(shù)變成一個單純的委托函數(shù),或是將舊函數(shù)完全移除。
7.2 Move Field 搬移字段
在你的程序中,某個字段被其所在類之外的另一個類更多地用到。
在目標類新建一個字段,修改源字段的所有用戶,令他們改用新字段。
7.3 Extract Class 提煉類
某個類做了應(yīng)該有兩個類做的事。
建立一個新類,將相關(guān)的字段和函數(shù)從舊類搬移到新類。
7.4 Inline Class 將類內(nèi)聯(lián)化
某個類沒有做太多的事情。
將這個類的所有特性搬移到另一個類中,然后移除原類。
7.5 Hide Delegate 隱藏委托關(guān)系
客戶通過一個委托類來調(diào)用另一個對象。
具體:如果某個客戶先通過服務(wù)對象的字段得到另一個對象,然后調(diào)用后者的函數(shù),那么客戶就必須知曉這一層委托關(guān)系。
在服務(wù)類上建立客戶所需的所有函數(shù),用以隱藏委托關(guān)系。
7.6 Remove Middle Man 移除中間人
某個類做了過多的簡單委托動作
讓客戶直接調(diào)用委托類。
7.7 Introduce Foreign Method 引入外加函數(shù)
你需要為提供服務(wù)的類增加一個函數(shù),但你無法修改這個類。
在客戶類中建立一個函數(shù),并以第一參數(shù)形式傳入一個服務(wù)類實例。
Date start = new Date(priviousEnd.getYear(), previous.getMonth(), privious.getDate() + 1);
Refactoring:
Date start = nextDay(previousEnd);
private static Date nextDay(Date arg) {
return new Date (arg.getYear(), arg.getMonth(), arg.getDate() + 1);
}
7.8 Introduce Local Extension 引入本地擴展
你需要為服務(wù)類提供一些額外函數(shù),但你無法修改這個類。
建立一個新類,是它包含這些額外函數(shù)。讓這個擴展品成為袁類的子類或包裝類。
Ch 8 重新組織數(shù)據(jù)
8.1 Self Encapsulate Field 自封裝字段
你直接訪問一個字段,但與字段之間的耦合關(guān)系逐漸變得笨拙。
為這個字段建立取值/設(shè)置函數(shù),并且只以這些函數(shù)來訪問字段。
8.2 Replace Data Value with Object 以對象取代數(shù)據(jù)值
你有一個數(shù)據(jù)項,需要與其他數(shù)據(jù)和行為一起使用才有意義。
將數(shù)據(jù)項變成對象
8.3 Change Value to Reference 將值對象改為引用對象
你從一個類衍生出許多彼此相等的實例,希望將它們替換為同一個對象。
將這個值對象變成引用對象。
8.4 Change Reference to Value
你有一個引用對象,很小且不可變,而且不易管理。
將它變?yōu)橐粋€值對象。
8.5 Replace Array with Object
你有一個數(shù)據(jù)組,其中的元素各自代表不同的東西。
以對象替代數(shù)組,對于數(shù)組中的每個元素,以一個字段來表示。
8.6 Duplicate Observed Data 復制被監(jiān)視數(shù)據(jù)
你有一些淋浴數(shù)據(jù)置身于GUI控件中,而領(lǐng)域函數(shù)需要訪問這些數(shù)據(jù)。
將該數(shù)據(jù)復制到一個領(lǐng)域?qū)ο笾校⒁粋€Observer 模式,用以同步領(lǐng)域?qū)ο蠛虶UI對象內(nèi)的重復數(shù)據(jù)。
8.7 Change Unidirectional Association to Bidirectional
連個類都需要使用對方特性,但其間只有一條單向連接。
添加一個反向指針,并使修改函數(shù)能夠同時更新兩條連接。
8.8 Change Bidirectional Association to Unidirectional
兩個雷直接有雙向關(guān)聯(lián),但其中一個類如今不再需要另一個類的特性。
去除不必要的關(guān)聯(lián)。
8.9 Replace Magic Number with Symbolic Constant
8.10 Encapsulate Field
- 11 Encapsulate Collection
有一個函數(shù)返回一個集合。
讓這個函數(shù)返回該集合的一個只讀副本,并在這個類中提供添加/移除集合元素的函數(shù)。
集合的取值函數(shù)不應(yīng)該返回集合本身,因為這會讓用戶得以修改結(jié)合內(nèi)容而集合擁有卻一無所知。
8.12 Replace Record with Data Class 以數(shù)據(jù)類取代記錄
你需要面對傳統(tǒng)編程環(huán)境中的記錄結(jié)構(gòu)。
為該結(jié)構(gòu)建立一個啞數(shù)據(jù)對象
8.13 Replace Type Code with Class
類之中有一個數(shù)值類型碼,但他并不影響類的行為。
以一個新的類替代該數(shù)值類型碼。
More Important:
任何switch語句都應(yīng)該運用Replace Conditional with Polymorphism去掉
8.14 Replace Type Code with Subclasses
你有一個不可變的類編碼,它會影響類的行為。
以子類取代這個類型碼。
這種表達式可能有兩種表現(xiàn)形式:switch語句或者if-then-else結(jié)構(gòu),不論哪種形式,它們都是檢查類型碼值,并根據(jù)不同的值執(zhí)行不同的動作。
如果宿主類有了子類,就需要使用Replace Type Code with State/Strategy
Replace Type Code with Subclasses 的好處在于:它把“對不同行為的了解”從類用戶那兒轉(zhuǎn)移到了類自身。如果需要再加入新的行為變化,只需要添加一個類就行了。如果沒有多態(tài)機制,就必須找到所有條件表達式,并逐一修改它們。因此,如果未來還有可能加入新行為,這項重構(gòu)將特別有價值。
8.15 Replace Type Code with State/Strategy
你有一個類型碼,他會影響類的行為,但你無法通過繼承手法消除它。
以狀態(tài)對象取代類型碼。
8.16 Replace Subclasses with Fields
你的各個子類的唯一差別只在返回常量數(shù)據(jù)的函數(shù)身上。
修改這些函數(shù),使它們返回超類中某個新增字段,然后銷毀子類。
Ch 9 簡化條件表達式
9.1 Decompose Conditional 分解條件表達式
你有一個復雜的條件(if-then-else) 語句。
從if, then, else 三個段落中分別提煉出來獨立函數(shù)。
- 復雜的條件邏輯是最常導致復雜度上升的地點之一。
9.2 Consolidate Conditional Expression 合并條件表達式
你有一系列條件測試,都得到相同的結(jié)果。
將這些測試合并為一個條件表達式,并將這個條件表達式提煉成為一個獨立函數(shù)。
9.3 Consolidate Duplicate Conditional Fragments 合并重復的條件片段
在條件表達式的每個分支上有著相同的一段代碼。
將這段重復代碼搬移到條件表達式之外。
9.4 Remove Control Flag 移除控制標記
在一系列布爾表達式,某個變量帶有控制標記
以break語句或return語句取代控制標記。
9.5 Replace Nested Conditional with Guard Clauses
在函數(shù)中的條件邏輯使人難以看清正常的執(zhí)行路徑。
使用衛(wèi)語句表現(xiàn)所有的特殊情形。
如果某個條件極其罕見,就應(yīng)該單獨檢測該條件,并在該條件為真時立刻從函數(shù)返回。這樣的單獨檢測常常被稱為:衛(wèi)語句(Guard Clauses)
9.6 Replace Conditional with Polymorphism
你手上有個條件表達式,他根據(jù)對象類型的不同而選擇不同的行為。
將這個條件表達式的每個分支放進一個子類的覆寫函數(shù)中,然后將原始函數(shù)聲明為抽象函數(shù)。
9.7 Introduce Null Object
你需要再三檢查某個對象是否為null
將null值替換為null對象。
9.8 Introduce Assertion
某段代碼需要對程序狀態(tài)做出某種假設(shè)。
以斷言明確表達這種假設(shè)。
Ch 10 簡化函數(shù)調(diào)用
- 1 Rename Method
10.2 Add Parameter
10.3 Remove Parameter
10.4 Separate Query from Modifier 將查詢函數(shù)和修改函數(shù)分離
某個函數(shù)既返回對象狀態(tài)值,又修改對象狀態(tài)。
建立兩個不同的函數(shù),其中一個復制查詢,一個負責修改。
10.5 Parameterize Method
10.6 Replace Parameter with Explicit Method
你有一個函數(shù),其中完全取決于參數(shù)值而采取不同的行為。
針對該參數(shù)的每一個可能值,建立一個獨立函數(shù)。
10.7 Preserve Whole Object
你從某個對象中去除若干之,將他們作為某一次函數(shù)調(diào)用時的參數(shù)。
改為傳遞整個對象。
10.8 Replace Parameter with methods 以函數(shù)代替參數(shù)
對象調(diào)用哪個某個哈數(shù),并將所得結(jié)果作為參數(shù),傳遞給另一個函數(shù),而接受該參數(shù)的函數(shù)本身也能調(diào)用前一個函數(shù)。
讓參數(shù)接受者去除該項參數(shù),并直接調(diào)用前一個函數(shù)。
10.9 Introduce Parameter Object
某些參數(shù)總是很自然的同時出現(xiàn)。
以一個對象取代這些參數(shù)。
10.10 Remove Setting Method
類中的某個字段應(yīng)該在對象創(chuàng)建愛你是被設(shè)值,然后不再改變。
去掉該字段的所有設(shè)值函數(shù)。
10.11 Hide Method
有一個函數(shù),從來沒有被任何類用到。
將這個函數(shù)修改為private
1012 Replace Constructor with Factory Method
- 最顯而易見的動機:
在派生子類的過程中以工廠函數(shù)取代類型碼。
10.13 Encapsulate Downcast 封裝向下轉(zhuǎn)型
某個函數(shù)返回的對象,需要都函數(shù)調(diào)用者執(zhí)行向下轉(zhuǎn)型。
將向下轉(zhuǎn)型動作移到移到函數(shù)中。
10.14 Replace Error Code with Exception
某個函數(shù)返回一個特定的代碼,用以表示某種錯誤情況。
改為異常。
- 15 Replace Exception with Test
面對一個調(diào)用者可以預(yù)先檢查的條件,你拋出了一個異常。
修改調(diào)用者,使它在調(diào)用函數(shù)之前先做檢查。
Ch 11 處理概括關(guān)系
概括關(guān)系,Generalization,繼承關(guān)系
11.1 Pull Up Field
兩個子類擁有相同的字段。
11.2 Pull Up Method
有些函數(shù),在各個子類中產(chǎn)生完全相同的結(jié)果。
將該函數(shù)移至超類。
11.3 Pull Up Constructor Body
11.4 Push Down Method
11.5 Push Down Field
11.6 Extract Subclasses
類中某些特性只被某些實例用到
新建一個子類將上面所說的那一部分特性移到子類中。
11.7 Extract Superclass
兩個類有相似的特性
為這兩個類建立一個超累,將相同特性移至超類。
11.8 Extract Interface
若干客戶使用類接口中的同一子集,或者兩個類的接口有部分相同。
將相同的子集提煉到一個獨立接口中。
11.9 Collapse Hierarchy
超類和子類沒有太大區(qū)別。
將它們合為一體。
11.10 Form Template Method
你有一些子類,其中相應(yīng)的某些函數(shù)相同的順序執(zhí)行類似的操作,但各個操作的細節(jié)上有所不同。
將這些操作分別放進獨立函數(shù)中,并保持他們都有相同的簽名,于是原函數(shù)也就變得相同了。然后將原函數(shù)上移至超類。
- 繼承是避免重復行為的強大工具。
11.11 Replace Inheritance with Delegation
某個子類只能使用超類接口中的一部分,或是根本不需要繼承而來的數(shù)據(jù)。
在子類中新建一個字段用以保存超類,調(diào)整子類函數(shù),令它改而委托超類;然后去掉來兩者之間的繼承關(guān)系。
11.12 Replace Delegation with Inheritance
你在兩個類之間使用委托關(guān)系,并經(jīng)常為整個接口編寫許多極簡單的委托函數(shù)。
讓委托類繼承受托類。
Ch 12 大型重構(gòu)
12.1 Tease Apart Inheritance 梳理并分解繼承體系
某個繼承體系同時承擔兩項責任。
建立兩個繼承體系,并通過委托關(guān)系讓其中一個可以調(diào)用另一個。
12.2 Convert Procedural Design to Objects 將過程化設(shè)計轉(zhuǎn)化為對象設(shè)計
你手上有一些傳統(tǒng)過程化風格的代碼。
將數(shù)據(jù)記錄變成對象,將大塊的行為分成小塊,并將行為移入相關(guān)對象之中。
12.3 Separate Domain from Presentation
某些類GUI類包含了領(lǐng)域邏輯。
江鈴域邏輯風里出來,為他們建立獨立的領(lǐng)域類。
12.4 Extract Hierarchy
你有某個類做了太多工作,其中一部分工作是以大量條件表達式完成的。
建立繼承體系,一一個子類展示一種特殊情況。