RefactoringGuru 代碼異味和重構(gòu)技巧總結(jié)

整理自 RefactoringGuru

代碼異味

——什么?代碼如何“聞味道”??

——它沒有鼻子...但它肯定會發(fā)臭!

代碼膨脹

【代碼膨脹】是代碼、方法和類,它們的規(guī)模已經(jīng)增加到了難以處理的地步。通常,這些異味不會立即出現(xiàn),而是隨著程序的演化而積累(尤其是當(dāng)沒有人努力根除它們的時候)。

過長方法

方法包含的代碼行太多。一般來說,任何超過十行的方法都會讓你產(chǎn)生疑問。

過大的類

一個類包含許多字段/方法/代碼行。

基本類型偏執(zhí)

在簡單任務(wù)中使用基本類型而不是小對象(例如貨幣、范圍、電話號碼的特殊字符串等)

使用常量來編碼信息(例如常量USER_ADMIN_ROLE=1表示具有管理員權(quán)限的用戶。)

在數(shù)據(jù)數(shù)組中使用字符串常量作為字段名。

過長參數(shù)列表

一個方法有三個或四個以上的參數(shù)。

數(shù)據(jù)泥團

有時,代碼的不同部分包含相同的變量組(例如用于連接數(shù)據(jù)庫的參數(shù))。這些組應(yīng)該轉(zhuǎn)化為它們自己的類。

面向?qū)ο鬄E用

所有這些異味都是面向?qū)ο缶幊淘淼牟煌暾虿徽_應(yīng)用。

switch語句

你有一個復(fù)雜的switch運算符或if語句序列。

臨時字段

臨時字段僅在特定情況下獲取其值(因此對象需要它)。除此之外,它們是空的。

被拒絕的繼承

如果子類只使用從其父類繼承的一些方法和屬性,那么層次結(jié)構(gòu)就不正常。不需要的方法可能只是不使用,或者被重新定義并發(fā)出異常。

具有不同接口的備選類

兩個類具有相同的函數(shù),但方法名不同。

更改的阻礙

這些異味意味著,如果你需要在代碼的某個地方更改某些內(nèi)容,那么你也必須在其他地方進行許多更改。因此,程序開發(fā)變得更加復(fù)雜和昂貴。

發(fā)散式更改

在更改類的時候,你發(fā)現(xiàn)自己必須更改許多不相關(guān)的方法。例如,添加新產(chǎn)品類型時,必須更改查找、展示和訂購產(chǎn)品的方法。

散彈式更改

修改任何東西都需要對許多不同的類做出許多小的更改。

平行繼承體系

每當(dāng)你為一個類創(chuàng)建一個子類時,你就會發(fā)現(xiàn)自己需要為另一個類創(chuàng)建一個子類。

可有可無的東西

可有可無的東西是毫無意義和不必要的,如果沒有它,代碼就會更干凈、更高效、更容易理解。

注釋

方法中充滿了解釋性注釋。

重復(fù)代碼

兩段代碼看起來幾乎相同。

冗余類

理解和維護類總是需要花費時間和金錢。因此,如果一個類不足以吸引你的注意力,它應(yīng)該被刪除。

數(shù)據(jù)類

數(shù)據(jù)類是指只包含字段和用于訪問字段的方法(獲取器和設(shè)置器)的類。這些只是其他類使用的數(shù)據(jù)容器。這些類不包含任何附加功能,并且不能獨立操作它們所擁有的數(shù)據(jù)。

死代碼

變量、參數(shù)、字段、方法或類已不再使用(通常是因為它已過時)。

夸大通用性

存在未使用的類、方法、字段或參數(shù)。

耦合器

這一組中的所有異味都會導(dǎo)致類之間的過度耦合,或者顯示如果耦合被過度委托所取代會發(fā)生什么。

功能依賴

一個方法訪問另一個對象的數(shù)據(jù)多于它自己的數(shù)據(jù)。

過度親密

一個類使用另一個類的內(nèi)部字段和方法。

消息鏈

在代碼中可以看到一系列類似于$a->b()->c()->d()的調(diào)用。

中間人

如果一個類只執(zhí)行一個操作,將工作委托給另一個類,那么它為什么存在呢?

其他異味

不完善的庫類

庫遲早會停止?jié)M足用戶需求。由于庫是只讀的,所以問題的唯一解決方案,也就是更改庫,通常是不可能的。

重構(gòu)技巧

組合方法

很多重構(gòu)都致力于正確地組合方法。在大多數(shù)情況下,過長的方法是萬惡之源。這些方法中變幻莫測的代碼隱藏了執(zhí)行邏輯,使得該方法極難理解,甚至更難更改。

這一組中的重構(gòu)技巧簡化了方法,消除了代碼重復(fù),并為未來的改進鋪平了道路。

提取方法

問題:你有一個可以組合在一起的代碼片段。

解決方案:將此代碼移動到一個單獨的新方法(或函數(shù)),并用對該方法的調(diào)用替換舊代碼。

內(nèi)聯(lián)函數(shù)

問題:當(dāng)方法主體比方法本身更明顯時,請使用此技巧。

解決方案:用方法的內(nèi)容替換對方法的調(diào)用,并刪除方法本身。

提取變量

問題:你的表達式很難理解。

解決方案:將表達式或其部分的結(jié)果放在獨立的變量中,這些變量是自解釋的。

內(nèi)聯(lián)臨時變量

問題:你有一個臨時變量,它被分配了一個簡單表達式的結(jié)果,僅此而已。

解決方案:用表達式本身替換對變量的引用。

用查詢替換臨時變量

問題:將表達式的結(jié)果放在局部變量中,以便以后在代碼中使用。

解決方案:將整個表達式移動到一個單獨的方法,并從中返回結(jié)果。查詢方法,而不是使用變量。如有必要,在其他方法中加入新方法。

拆分臨時變量

問題:你有一個局部變量,用于在方法中存儲各種中間值(循環(huán)變量除外)。

解決方案:對不同的值使用不同的變量。每個變量應(yīng)該只負責(zé)一個特定的事情。

移除參數(shù)賦值

問題:某些值被賦給了方法體中的參數(shù)。

解決方案:使用局部變量而不是參數(shù)。

用方法對象替換方法

問題:你有一個很長的方法,其中局部變量相互交織,以至于你不能應(yīng)用【提取方法】。

解決方案:將該方法轉(zhuǎn)換為一個單獨的類,以便局部變量成為該類的字段。然后可以將該方法拆分為同一類中的多個方法。

替代算法

問題:所以你想用一個新的算法替換現(xiàn)有的算法?

解決方案:用新算法替換實現(xiàn)算法的方法體。

在對象間移動功能

即使你在不同的類之間,以不太完美的方式分布了功能,仍然存在希望。

這些重構(gòu)技術(shù)展示了如何在類之間安全地移動功能,創(chuàng)建新的類,以及隱藏實現(xiàn)細節(jié)以防公開訪問。

移動方法

問題:一個方法在另一個類中使用的次數(shù)多于在它自己的類中使用的次數(shù)。

解決方案:在使用該方法最多的類中創(chuàng)建一個新方法,然后將代碼從舊方法移動到這里。將舊方法的代碼轉(zhuǎn)換為對另一個類中新方法的引用,或者將其完全刪除。

移動字段

問題:一個字段在另一個類中使用的次數(shù)比在它自己的類中使用的次數(shù)多。

解決方案:在新類中創(chuàng)建一個字段,并將舊字段的所有使用重定向到該字段。

提取類

問題:當(dāng)一個類做兩個類的工作時,會非常笨拙。

解決方案:相反,創(chuàng)建一個新類,并將負責(zé)相關(guān)功能的字段和方法放在其中。

內(nèi)聯(lián)類

問題:一個類幾乎什么都不做,也不負責(zé)任何事情,也沒有為它規(guī)劃額外的責(zé)任。

解決方案:將所有功能從該類移動到另一個類。

隱藏委托關(guān)系

問題:客戶端從對象 A 的字段或方法中獲取對象 B。然后客戶端調(diào)用對象 B 的方法。

解決方案:在類 A 中創(chuàng)建一個新方法,將調(diào)用委托給對象 B。現(xiàn)在客戶端不知道也不依賴于類 B。

移除中間人

問題:一個類有太多的方法,這些方法只是委托給其他對象。

解決方案:刪除這些方法,并強制客戶端直接調(diào)用最終方法。

引入外部方法

問題:實用程序類不包含所需的方法,并且無法將該方法添加到該類中。

解決方案:將該方法添加到客戶端類,并將實用程序類的對象作為參數(shù)傳遞給它。

引入本地擴展

問題:實用程序類不包含你需要的某些方法。但不能將這些方法添加到類中。

解決方案:創(chuàng)建一個包含這些方法的新類,并使其成為實用程序類的子類或包裝器。

組織數(shù)據(jù)

這些重構(gòu)技術(shù)有助于數(shù)據(jù)處理,用豐富的類功能替換基本類型。

另一個重要的結(jié)果是解開了類的關(guān)聯(lián),這使得類更具可移植性和可重用性。

自封裝字段

問題:你直接訪問類內(nèi)的私有字段。

解決方案:為字段創(chuàng)建一個獲取器和設(shè)置器,并僅使用它們訪問字段。

用對象替換數(shù)據(jù)值

問題:一個類(或一組類)包含一個數(shù)據(jù)字段。該字段有自己的行為和相關(guān)數(shù)據(jù)。

解決方案:創(chuàng)建一個新類,將舊字段及其行為放在該類中,并將該類的對象存儲在原始類中。

將值更改為引用

問題:所以你有單個類的許多相同實例,并需要用單個對象替換它。

解決方案:將相同的對象轉(zhuǎn)換為單個引用對象。

將引用更改為值

問題:你有一個太小且很少更改的引用對象,因此無法管理其生命周期。

解決方案:將其轉(zhuǎn)化為值對象。

用對象替換數(shù)組

問題:你有一個包含各種類型數(shù)據(jù)的數(shù)組。

解決方案:將數(shù)組替換為每個元素都有單獨字段的對象。

重復(fù)的被觀測數(shù)據(jù)

問題:存儲在類中的領(lǐng)域數(shù)據(jù)是否負責(zé)GUI?

解決方案:那么最好將數(shù)據(jù)分成不同的類,確保領(lǐng)域類和GUI之間的連接和同步。

將單向關(guān)聯(lián)改為雙向關(guān)聯(lián)

問題:你有兩個類,每個類都需要使用另一個類的功能,但它們之間的關(guān)聯(lián)只是單向的。

解決方案:將缺少的關(guān)聯(lián)添加到需要它的類中。

將雙向關(guān)聯(lián)改為單向關(guān)聯(lián)

問題:類之間存在雙向關(guān)聯(lián),但其中一個類不使用另一個類的功能。

解決方案:刪除未使用的關(guān)聯(lián)。

用符號常量替換幻數(shù)

問題:你的代碼使用了一個具有特定含義的數(shù)字。

解決方案:將這個數(shù)字替換為一個常量,該常量有一個人類可讀的名稱來解釋數(shù)字的含義。

封裝字段

問題:你有一個公共字段。

解決方案:將字段設(shè)置為私有,并為其創(chuàng)建訪問方法。

封裝集合

問題:一個類包含一個集合字段和一個用于處理集合的簡單獲取器和設(shè)置器。

解決方案:將獲取器的返回值設(shè)為只讀,并創(chuàng)建用于添加/刪除集合元素的方法。

用類替換類型代碼

問題:一個類有一個包含類型代碼的字段。這種類型的值不用于運算符條件,也不會影響程序的行為。

解決方案:創(chuàng)建一個新類,并使用其對象而不是類型代碼的值。

用子類替換類型代碼

問題:你有一個直接影響程序行為的代碼類型(此字段的值觸發(fā)條件中的各種代碼)。

解決方案:為代碼類型的每個值創(chuàng)建子類。然后將相關(guān)行為從原始類提取到這些子類中。用多態(tài)替換控制流代碼。

用狀態(tài)/策略替換類型代碼

問題:你有一個影響行為的代碼類型,但不能使用子類來消除它。

解決方案:用狀態(tài)對象替換類型代碼。如果需要用類型代碼替換字段值,則另一個狀態(tài)對象為“已插入”。

用字段替換子類

問題:你的子類只在(常量返回)方法上有所不同。

解決方案:用父類中的字段替換方法,并刪除子類。

簡化條件表達式

隨著時間的推移,條件的邏輯往往變得越來越復(fù)雜,還有更多的技術(shù)可以解決這個問題。

分解條件

問題:你有一個復(fù)雜的條件(if-then/elseswitch)。

解決方案:將條件的復(fù)雜部分分解為單獨的方法:條件、thenelse。

合并條件表達式

問題:你有多個條件產(chǎn)生相同的結(jié)果或操作。

解決方案:將所有這些條件合并到一個表達式中。

合并重復(fù)的條件片段

問題:在條件語句的所有分支中都可以找到相同的代碼。

解決方案:將代碼移到條件之外。

移除控制標(biāo)志

問題:有一個布爾變量充當(dāng)多個布爾表達式的控制標(biāo)志。

解決方案:使用break、continuereturn代替變量。

使用守衛(wèi)子句來代替嵌套的條件判斷

問題:有一組嵌套的條件,很難確定代碼執(zhí)行的正常流程。

解決方案:將所有特殊檢查和邊界情況隔離到單獨的子句中,并將其放在主要檢查之前。理想情況下,你應(yīng)該有一個條件列表,一個接一個。

用多態(tài)替換條件

問題:你有一個條件,根據(jù)對象類型或?qū)傩詧?zhí)行各種操作。

解決方案:創(chuàng)建與條件的分支相匹配的子類。在它們中,創(chuàng)建一個共享方法,并將代碼從條件的相應(yīng)分支移動到它。然后用相關(guān)的方法調(diào)用替換條件。結(jié)果是,根據(jù)對象類,可以通過多態(tài)實現(xiàn)正確的實現(xiàn)。

引入空對象

問題:由于一些方法返回null而不是真實對象,所以在代碼中有很多null檢查。

解決方案:返回一個顯示默認行為的空對象,而不是null。

引入斷言

問題:要使部分代碼正常工作,某些條件或值必須為true

解決方案:用特定的斷言檢查替換這些假設(shè)。

簡化方法調(diào)用

這些技術(shù)使方法調(diào)用更簡單、更容易理解。這反過來簡化了用于類之間交互的接口。

重命名方法

問題:方法的名稱不能解釋該方法的功能。

解決方案:重命名該方法。

添加參數(shù)

問題:方法沒有足夠的數(shù)據(jù)來執(zhí)行某些操作。

解決方案:創(chuàng)建一個新參數(shù)來傳遞必要的數(shù)據(jù)。

刪除參數(shù)

問題:方法體中沒有使用某個參數(shù)。

解決方案:刪除未使用的參數(shù)。

將查詢與修改分開

問題:是否有一個方法可以返回一個值,但也可以更改對象內(nèi)部的某些內(nèi)容?

解決方案:將該方法分為兩種不同的方法。正如你所料,其中一個應(yīng)該返回值,另一個則修改對象。

將方法參數(shù)化

問題:多個方法執(zhí)行類似的操作,這些操作只在其內(nèi)部值、數(shù)字或操作上有所不同。

解決方案:通過使用一個將傳遞必要特殊值的參數(shù)來組合這些方法。

用顯式方法替換參數(shù)

問題:一個方法被分成幾個部分,每個部分的運行取決于一個參數(shù)的值。

解決方案:將方法的各個部分提取到它們自己的方法中,并調(diào)用它們,而不是原始方法。

保存整個對象

問題:從一個對象中獲取多個值,然后將它們作為參數(shù)傳遞給一個方法。

解決方案:相反,嘗試傳遞整個對象。

用方法調(diào)用替換參數(shù)

問題:調(diào)用一個查詢方法并將其結(jié)果作為參數(shù)傳遞給另一個方法,而該方法可以直接調(diào)用該查詢。

解決方案:不要通過參數(shù)傳遞值,而是嘗試在方法體中放置一個查詢調(diào)用。

引入?yún)?shù)對象

問題:你的方法包含一組重復(fù)的參數(shù)。

解決方案:用對象替換這些參數(shù)。

移除設(shè)置方法

問題:字段的值應(yīng)該只在創(chuàng)建時設(shè)置,之后任何時候都不能更改。

解決方案:刪除設(shè)置字段值的方法。

隱藏方法

問題:一個方法不被其他類使用,或者只在它自己的類層次結(jié)構(gòu)中使用。

解決方案:將方法設(shè)置為私有或受保護。

用工廠方法代替構(gòu)造器

問題:你有一個復(fù)雜的構(gòu)造器,它的功能不僅僅是在對象字段中設(shè)置參數(shù)值。

解決方案:創(chuàng)建一個工廠方法并使用它替換構(gòu)造器調(diào)用。

用異常替換錯誤代碼

問題:方法返回指示錯誤的特殊值?

解決方案:拋出一個異常。

用測試替換異常

問題:在一個簡單的測試就能完成任務(wù)的地方拋出異常?

解決方案:用條件測試替換異常。

處理泛化

抽象有自己的一組重構(gòu)技術(shù),主要關(guān)于沿著類繼承層次結(jié)構(gòu)移動功能、創(chuàng)建新的類和接口、用委托代替繼承以及相反。

上移字段

問題:兩個類具有相同的字段。

解決方案:從子類中刪除字段,并將其移動到超類。

上移方法

問題:你的子類具有執(zhí)行類似工作的方法。

解決方案:使方法相同,然后將它們移動到相關(guān)的超類。

上移構(gòu)造器主體

問題:你的子類的構(gòu)造器的代碼基本相同。

解決方案:創(chuàng)建一個超類構(gòu)造器,并將子類中相同的代碼移動到它。在子類構(gòu)造器中調(diào)用超類構(gòu)造器。

下移方法

問題:超類中實現(xiàn)的行為是僅由一個(或幾個)子類使用的嗎?

解決方案:將此行為移動到子類。

下移字段

問題:字段是否僅用于少數(shù)子類?

解決方案:將字段移動到這些子類。

提取子類

問題:某個類具有僅在某些情況下使用的功能。

解決方案:創(chuàng)建一個子類,并在這些情況下使用它。

提取超類

問題:有兩個類具有相同的字段和方法。

解決方案:為它們創(chuàng)建一個共享超類,并將所有相同的字段和方法移動到其中。

提取接口

問題:多個客戶端使用類接口的同一部分。另一種情況:兩個類中的部分接口是相同的。

解決方案:將這個相同的部分移動到它自己的接口。

折疊層次結(jié)構(gòu)

問題:你有一個類層次結(jié)構(gòu),其中一個子類實際上與其超類相同。

解決方案:合并子類和超類。

形成模板方法

問題:你的子類實現(xiàn)的算法包含順序相同的類似步驟。

解決方案:將算法結(jié)構(gòu)和相同的步驟移動到一個超類,并將不同步驟的實現(xiàn)留在子類中。

用委托替換繼承

問題:有一個子類只使用其超類的一部分方法(或者不可能繼承超類數(shù)據(jù))。

解決方案:創(chuàng)建一個字段并在其中放置一個超類對象,將方法委托給超類對象,并擺脫繼承。

用繼承替換委托

問題:一個類包含許多簡單的方法,這些方法將委托給另一個類的所有方法。

解決方案:使該類繼承另一個類,這樣就不需要委托方法。

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

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

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