Refactoring: Improving the Design of Existing Code

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 異曲同工的類

  1. 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

  1. 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. 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ù)返回一個特定的代碼,用以表示某種錯誤情況。

改為異常。

  1. 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
你有某個類做了太多工作,其中一部分工作是以大量條件表達式完成的。

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

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,527評論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,687評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,640評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,957評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,682評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,011評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,009評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,183評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,714評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,435評論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,665評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,148評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,838評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,251評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,588評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,379評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,627評論 2 380

推薦閱讀更多精彩內(nèi)容

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