Refactoring: Improving the Design of Existing Code

ref: 重構(gòu):改善既有代碼的設(shè)計(jì)
Refactoring: Improving the Design of Existing Code

Ch 1 重構(gòu),第一個(gè)案例

重構(gòu)的第一個(gè)步驟永遠(yuǎn)相同:我得為即將修改的代碼建立一組可靠的測(cè)試環(huán)境。

好的測(cè)試是重構(gòu)的根本。

Ch 2 重構(gòu)原則

重構(gòu):對(duì)于軟件內(nèi)部結(jié)構(gòu)的一種調(diào)整,目的是在不改變軟件可觀察行為的前提下,提高其可理解性,降低其修改成本。

Ch 3 代碼的壞味道

3.1 Duplicated Code 重復(fù)代碼
3.2 Long Method 過程函數(shù)
更加積極的分解函數(shù)。
原則:每當(dāng)感覺需要以注釋來說明點(diǎn)什么的時(shí)候,我們就把需要說明 東西寫進(jìn)一個(gè)獨(dú)立的函數(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ù)泥團(tuán)

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 令人迷惑的暫時(shí)字段

3.15 Message Chains 過度耦合的消息鏈

3.16 Middle Man 中間人

3.17 Inappropriate Intimacy 狎昵關(guān)系

3.18 Alternative Classes with Different Intefaces 異曲同工的類

  1. 19 Imcomplete Library Class 不完美的庫類

3.20 Data Class 數(shù)據(jù)類

3.21 Befused Bequest 被拒絕的遺贈(zèng)

3.22 Comments 過多的注釋

但你感覺需要撰寫注釋時(shí),請(qǐng)先嘗試重構(gòu),試著讓所有注釋夠變得多余。

Ch 4 構(gòu)筑測(cè)試體系

每當(dāng)你收到bug報(bào)告,前先寫一個(gè)單元測(cè)試來暴露這個(gè)bug。

Ch 5 重構(gòu)列表

Ch 6 重新組織函數(shù)

6.1 Extract Method 提煉函數(shù)
6.2 Inline Method 內(nèi)聯(lián)函數(shù)
一個(gè)函數(shù)的本體與名稱同樣清楚易懂。
6.3 Inline Temp 內(nèi)聯(lián)臨時(shí)變量
6.4 Replace Temp with Query 以查詢?nèi)〈R時(shí)變量
你的程序以一個(gè)臨時(shí)變量保存某一表達(dá)式的運(yùn)算結(jié)果。

將表達(dá)式提煉到一個(gè)獨(dú)立的函數(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 引入解釋性變量
你有一個(gè)復(fù)雜的表達(dá)式

將該復(fù)雜表達(dá)式(或其中一部分)的結(jié)果放進(jìn)一個(gè)臨時(shí)變量,以此變量名稱來解釋表達(dá)式的用途

6.6 Split Temporary Variables 分解臨時(shí)變量

你的程序有某個(gè)臨時(shí)變量被賦值超過一次,它既不是循環(huán)變量,也不被用與收集計(jì)算結(jié)果。

針對(duì)每次賦值,創(chuàng)建一個(gè)獨(dú)立、對(duì)應(yīng)的臨時(shí)變量。

同一個(gè)臨時(shí)變量承擔(dān)兩件不同的事情,會(huì)令代碼閱讀者糊涂。

6.7 Remove Assignments to Parameters 移除對(duì)參數(shù)的賦值

代碼對(duì)一個(gè)參數(shù)進(jìn)行賦值

以一個(gè)臨時(shí)變量取代該參數(shù)的位置。

6.8 Replace Method with Method Object 以函數(shù)對(duì)象取代函數(shù)
你有一個(gè)大型函數(shù),其中對(duì)局部對(duì)象的使用是你無法采用Extract Method

將這個(gè)函數(shù)放進(jìn)一個(gè)單獨(dú)對(duì)象中,如此一來局部變量就成了對(duì)象內(nèi)的字段。然后你可以在同一個(gè)對(duì)象中將這個(gè)大型函數(shù)分解為多個(gè)小型函數(shù),

6.9 Substitute Algorithm 替換算法
你想要把某個(gè)算法替換為另一個(gè)更清晰的算法。

將函數(shù)本體替換為另一個(gè)算法。

Ch 7 在對(duì)象之間搬移特性

7.1 Move Method 搬移函數(shù)

你的程序中,有個(gè)函數(shù)與其所在的類之外的另一個(gè)類進(jìn)行更多交流:調(diào)用后者,或被后者調(diào)用。

在該函數(shù)最常引用的類中建立一個(gè)有著類似行為的新函數(shù)。將舊函數(shù)變成一個(gè)單純的委托函數(shù),或是將舊函數(shù)完全移除。

7.2 Move Field 搬移字段

在你的程序中,某個(gè)字段被其所在類之外的另一個(gè)類更多地用到。

在目標(biāo)類新建一個(gè)字段,修改源字段的所有用戶,令他們改用新字段。

7.3 Extract Class 提煉類

某個(gè)類做了應(yīng)該有兩個(gè)類做的事。

建立一個(gè)新類,將相關(guān)的字段和函數(shù)從舊類搬移到新類。

7.4 Inline Class 將類內(nèi)聯(lián)化

某個(gè)類沒有做太多的事情。

將這個(gè)類的所有特性搬移到另一個(gè)類中,然后移除原類。

7.5 Hide Delegate 隱藏委托關(guān)系

客戶通過一個(gè)委托類來調(diào)用另一個(gè)對(duì)象。

具體:如果某個(gè)客戶先通過服務(wù)對(duì)象的字段得到另一個(gè)對(duì)象,然后調(diào)用后者的函數(shù),那么客戶就必須知曉這一層委托關(guān)系。

在服務(wù)類上建立客戶所需的所有函數(shù),用以隱藏委托關(guān)系。

7.6 Remove Middle Man 移除中間人

某個(gè)類做了過多的簡(jiǎn)單委托動(dòng)作

讓客戶直接調(diào)用委托類。

7.7 Introduce Foreign Method 引入外加函數(shù)
你需要為提供服務(wù)的類增加一個(gè)函數(shù),但你無法修改這個(gè)類。

在客戶類中建立一個(gè)函數(shù),并以第一參數(shù)形式傳入一個(gè)服務(wù)類實(shí)例。

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 引入本地?cái)U(kuò)展
你需要為服務(wù)類提供一些額外函數(shù),但你無法修改這個(gè)類。

建立一個(gè)新類,是它包含這些額外函數(shù)。讓這個(gè)擴(kuò)展品成為袁類的子類或包裝類。

Ch 8 重新組織數(shù)據(jù)

8.1 Self Encapsulate Field 自封裝字段
你直接訪問一個(gè)字段,但與字段之間的耦合關(guān)系逐漸變得笨拙。

為這個(gè)字段建立取值/設(shè)置函數(shù),并且只以這些函數(shù)來訪問字段。

8.2 Replace Data Value with Object 以對(duì)象取代數(shù)據(jù)值

你有一個(gè)數(shù)據(jù)項(xiàng),需要與其他數(shù)據(jù)和行為一起使用才有意義。

將數(shù)據(jù)項(xiàng)變成對(duì)象

8.3 Change Value to Reference 將值對(duì)象改為引用對(duì)象
你從一個(gè)類衍生出許多彼此相等的實(shí)例,希望將它們替換為同一個(gè)對(duì)象。

將這個(gè)值對(duì)象變成引用對(duì)象。

8.4 Change Reference to Value
你有一個(gè)引用對(duì)象,很小且不可變,而且不易管理。

將它變?yōu)橐粋€(gè)值對(duì)象。

8.5 Replace Array with Object
你有一個(gè)數(shù)據(jù)組,其中的元素各自代表不同的東西。

以對(duì)象替代數(shù)組,對(duì)于數(shù)組中的每個(gè)元素,以一個(gè)字段來表示。

8.6 Duplicate Observed Data 復(fù)制被監(jiān)視數(shù)據(jù)

你有一些淋浴數(shù)據(jù)置身于GUI控件中,而領(lǐng)域函數(shù)需要訪問這些數(shù)據(jù)。

將該數(shù)據(jù)復(fù)制到一個(gè)領(lǐng)域?qū)ο笾?,建立一個(gè)Observer 模式,用以同步領(lǐng)域?qū)ο蠛虶UI對(duì)象內(nèi)的重復(fù)數(shù)據(jù)。

8.7 Change Unidirectional Association to Bidirectional
連個(gè)類都需要使用對(duì)方特性,但其間只有一條單向連接。

添加一個(gè)反向指針,并使修改函數(shù)能夠同時(shí)更新兩條連接。

8.8 Change Bidirectional Association to Unidirectional
兩個(gè)雷直接有雙向關(guān)聯(lián),但其中一個(gè)類如今不再需要另一個(gè)類的特性。

去除不必要的關(guān)聯(lián)。

8.9 Replace Magic Number with Symbolic Constant

8.10 Encapsulate Field

  1. 11 Encapsulate Collection
    有一個(gè)函數(shù)返回一個(gè)集合。

讓這個(gè)函數(shù)返回該集合的一個(gè)只讀副本,并在這個(gè)類中提供添加/移除集合元素的函數(shù)。

集合的取值函數(shù)不應(yīng)該返回集合本身,因?yàn)檫@會(huì)讓用戶得以修改結(jié)合內(nèi)容而集合擁有卻一無所知。

8.12 Replace Record with Data Class 以數(shù)據(jù)類取代記錄
你需要面對(duì)傳統(tǒng)編程環(huán)境中的記錄結(jié)構(gòu)。

為該結(jié)構(gòu)建立一個(gè)啞數(shù)據(jù)對(duì)象

8.13 Replace Type Code with Class

類之中有一個(gè)數(shù)值類型碼,但他并不影響類的行為。

以一個(gè)新的類替代該數(shù)值類型碼。

More Important:
任何switch語句都應(yīng)該運(yùn)用Replace Conditional with Polymorphism去掉

8.14 Replace Type Code with Subclasses

你有一個(gè)不可變的類編碼,它會(huì)影響類的行為。

以子類取代這個(gè)類型碼。

這種表達(dá)式可能有兩種表現(xiàn)形式:switch語句或者if-then-else結(jié)構(gòu),不論哪種形式,它們都是檢查類型碼值,并根據(jù)不同的值執(zhí)行不同的動(dòng)作。

  • 如果宿主類有了子類,就需要使用Replace Type Code with State/Strategy

  • Replace Type Code with Subclasses 的好處在于:它把“對(duì)不同行為的了解”從類用戶那兒轉(zhuǎn)移到了類自身。如果需要再加入新的行為變化,只需要添加一個(gè)類就行了。如果沒有多態(tài)機(jī)制,就必須找到所有條件表達(dá)式,并逐一修改它們。因此,如果未來還有可能加入新行為,這項(xiàng)重構(gòu)將特別有價(jià)值。

8.15 Replace Type Code with State/Strategy

你有一個(gè)類型碼,他會(huì)影響類的行為,但你無法通過繼承手法消除它。

以狀態(tài)對(duì)象取代類型碼。

8.16 Replace Subclasses with Fields

你的各個(gè)子類的唯一差別只在返回常量數(shù)據(jù)的函數(shù)身上。

修改這些函數(shù),使它們返回超類中某個(gè)新增字段,然后銷毀子類。

Ch 9 簡(jiǎn)化條件表達(dá)式

9.1 Decompose Conditional 分解條件表達(dá)式

你有一個(gè)復(fù)雜的條件(if-then-else) 語句。

從if, then, else 三個(gè)段落中分別提煉出來獨(dú)立函數(shù)。

  • 復(fù)雜的條件邏輯是最常導(dǎo)致復(fù)雜度上升的地點(diǎn)之一。

9.2 Consolidate Conditional Expression 合并條件表達(dá)式

你有一系列條件測(cè)試,都得到相同的結(jié)果。

將這些測(cè)試合并為一個(gè)條件表達(dá)式,并將這個(gè)條件表達(dá)式提煉成為一個(gè)獨(dú)立函數(shù)。

9.3 Consolidate Duplicate Conditional Fragments 合并重復(fù)的條件片段

在條件表達(dá)式的每個(gè)分支上有著相同的一段代碼。

將這段重復(fù)代碼搬移到條件表達(dá)式之外。

9.4 Remove Control Flag 移除控制標(biāo)記

在一系列布爾表達(dá)式,某個(gè)變量帶有控制標(biāo)記

以break語句或return語句取代控制標(biāo)記。

9.5 Replace Nested Conditional with Guard Clauses
在函數(shù)中的條件邏輯使人難以看清正常的執(zhí)行路徑。

使用衛(wèi)語句表現(xiàn)所有的特殊情形。

如果某個(gè)條件極其罕見,就應(yīng)該單獨(dú)檢測(cè)該條件,并在該條件為真時(shí)立刻從函數(shù)返回。這樣的單獨(dú)檢測(cè)常常被稱為:衛(wèi)語句(Guard Clauses)

9.6 Replace Conditional with Polymorphism
你手上有個(gè)條件表達(dá)式,他根據(jù)對(duì)象類型的不同而選擇不同的行為。

將這個(gè)條件表達(dá)式的每個(gè)分支放進(jìn)一個(gè)子類的覆寫函數(shù)中,然后將原始函數(shù)聲明為抽象函數(shù)。

9.7 Introduce Null Object
你需要再三檢查某個(gè)對(duì)象是否為null

將null值替換為null對(duì)象。

9.8 Introduce Assertion
某段代碼需要對(duì)程序狀態(tài)做出某種假設(shè)。

以斷言明確表達(dá)這種假設(shè)。

Ch 10 簡(jiǎn)化函數(shù)調(diào)用

  1. 1 Rename Method

10.2 Add Parameter

10.3 Remove Parameter

10.4 Separate Query from Modifier 將查詢函數(shù)和修改函數(shù)分離

某個(gè)函數(shù)既返回對(duì)象狀態(tài)值,又修改對(duì)象狀態(tài)。

建立兩個(gè)不同的函數(shù),其中一個(gè)復(fù)制查詢,一個(gè)負(fù)責(zé)修改。

10.5 Parameterize Method

10.6 Replace Parameter with Explicit Method

你有一個(gè)函數(shù),其中完全取決于參數(shù)值而采取不同的行為。

針對(duì)該參數(shù)的每一個(gè)可能值,建立一個(gè)獨(dú)立函數(shù)。

10.7 Preserve Whole Object
你從某個(gè)對(duì)象中去除若干之,將他們作為某一次函數(shù)調(diào)用時(shí)的參數(shù)。

改為傳遞整個(gè)對(duì)象。

10.8 Replace Parameter with methods 以函數(shù)代替參數(shù)
對(duì)象調(diào)用哪個(gè)某個(gè)哈數(shù),并將所得結(jié)果作為參數(shù),傳遞給另一個(gè)函數(shù),而接受該參數(shù)的函數(shù)本身也能調(diào)用前一個(gè)函數(shù)。

讓參數(shù)接受者去除該項(xiàng)參數(shù),并直接調(diào)用前一個(gè)函數(shù)。

10.9 Introduce Parameter Object
某些參數(shù)總是很自然的同時(shí)出現(xiàn)。

以一個(gè)對(duì)象取代這些參數(shù)。

10.10 Remove Setting Method
類中的某個(gè)字段應(yīng)該在對(duì)象創(chuàng)建愛你是被設(shè)值,然后不再改變。

去掉該字段的所有設(shè)值函數(shù)。

10.11 Hide Method
有一個(gè)函數(shù),從來沒有被任何類用到。

將這個(gè)函數(shù)修改為private

1012 Replace Constructor with Factory Method

  • 最顯而易見的動(dòng)機(jī):
    在派生子類的過程中以工廠函數(shù)取代類型碼。

10.13 Encapsulate Downcast 封裝向下轉(zhuǎn)型
某個(gè)函數(shù)返回的對(duì)象,需要都函數(shù)調(diào)用者執(zhí)行向下轉(zhuǎn)型。

將向下轉(zhuǎn)型動(dòng)作移到移到函數(shù)中。

10.14 Replace Error Code with Exception

某個(gè)函數(shù)返回一個(gè)特定的代碼,用以表示某種錯(cuò)誤情況。

改為異常。

  1. 15 Replace Exception with Test
    面對(duì)一個(gè)調(diào)用者可以預(yù)先檢查的條件,你拋出了一個(gè)異常。

修改調(diào)用者,使它在調(diào)用函數(shù)之前先做檢查。

Ch 11 處理概括關(guān)系

概括關(guān)系,Generalization,繼承關(guān)系

11.1 Pull Up Field
兩個(gè)子類擁有相同的字段。

11.2 Pull Up Method

有些函數(shù),在各個(gè)子類中產(chǎn)生完全相同的結(jié)果。

將該函數(shù)移至超類。

11.3 Pull Up Constructor Body

11.4 Push Down Method

11.5 Push Down Field

11.6 Extract Subclasses
類中某些特性只被某些實(shí)例用到

新建一個(gè)子類將上面所說的那一部分特性移到子類中。

11.7 Extract Superclass
兩個(gè)類有相似的特性

為這兩個(gè)類建立一個(gè)超累,將相同特性移至超類。

11.8 Extract Interface

若干客戶使用類接口中的同一子集,或者兩個(gè)類的接口有部分相同。

將相同的子集提煉到一個(gè)獨(dú)立接口中。

11.9 Collapse Hierarchy
超類和子類沒有太大區(qū)別。

將它們合為一體。

11.10 Form Template Method
你有一些子類,其中相應(yīng)的某些函數(shù)相同的順序執(zhí)行類似的操作,但各個(gè)操作的細(xì)節(jié)上有所不同。

將這些操作分別放進(jìn)獨(dú)立函數(shù)中,并保持他們都有相同的簽名,于是原函數(shù)也就變得相同了。然后將原函數(shù)上移至超類。

  • 繼承是避免重復(fù)行為的強(qiáng)大工具。

11.11 Replace Inheritance with Delegation
某個(gè)子類只能使用超類接口中的一部分,或是根本不需要繼承而來的數(shù)據(jù)。

在子類中新建一個(gè)字段用以保存超類,調(diào)整子類函數(shù),令它改而委托超類;然后去掉來兩者之間的繼承關(guān)系。

11.12 Replace Delegation with Inheritance
你在兩個(gè)類之間使用委托關(guān)系,并經(jīng)常為整個(gè)接口編寫許多極簡(jiǎn)單的委托函數(shù)。

讓委托類繼承受托類。

Ch 12 大型重構(gòu)

12.1 Tease Apart Inheritance 梳理并分解繼承體系

某個(gè)繼承體系同時(shí)承擔(dān)兩項(xiàng)責(zé)任。

建立兩個(gè)繼承體系,并通過委托關(guān)系讓其中一個(gè)可以調(diào)用另一個(gè)。

12.2 Convert Procedural Design to Objects 將過程化設(shè)計(jì)轉(zhuǎn)化為對(duì)象設(shè)計(jì)

你手上有一些傳統(tǒng)過程化風(fēng)格的代碼。

將數(shù)據(jù)記錄變成對(duì)象,將大塊的行為分成小塊,并將行為移入相關(guān)對(duì)象之中。

12.3 Separate Domain from Presentation
某些類GUI類包含了領(lǐng)域邏輯。

江鈴域邏輯風(fēng)里出來,為他們建立獨(dú)立的領(lǐng)域類。

12.4 Extract Hierarchy
你有某個(gè)類做了太多工作,其中一部分工作是以大量條件表達(dá)式完成的。

建立繼承體系,一一個(gè)子類展示一種特殊情況。

Ch 13 重構(gòu),復(fù)用與現(xiàn)實(shí)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Refactoring Improving the Design of Existing Code Refacto...
    icecity96閱讀 570評(píng)論 0 0
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法,內(nèi)部類的語法,繼承相關(guān)的語法,異常的語法,線程的語...
    子非魚_t_閱讀 34,628評(píng)論 18 399
  • 《重構(gòu)》讀書筆記 總覽 第一部分 第一章從實(shí)例程序出發(fā),展示設(shè)計(jì)的缺陷,對(duì)其重構(gòu)可以了解重構(gòu)的過程和方法。 第二部...
    白樺葉閱讀 2,532評(píng)論 2 5
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,506評(píng)論 19 139
  • 有幾個(gè)死黨,現(xiàn)在各自漂泊著。一個(gè)在西安,一個(gè)在上海,一個(gè)在襄陽,而我在天津。有時(shí)候有了性質(zhì)和時(shí)間,就約在一起聚一聚...
    尋找靈族閱讀 160評(píng)論 0 1

友情鏈接更多精彩內(nèi)容