代碼本應優雅!識別哪些需要重構的“壞味道”

​重構:對軟件內部結構的一種調整,目的是在不改變軟件可觀察行為的前提下,提高其可理解性,降低其修改成本。

代碼本應優雅!識別哪些代碼中的“壞味道”是重構的起點。

Duplicated Code:重複代碼

重複代碼是首當其衝的“壞味道”,也是最易識別的重構點:相同或相似的代碼片段出現兩次或以上就可以稱之為重複代碼了。重複代碼的出現不單單是重複勞動(即使是拷貝),更為關鍵的問題是當重複代碼需要變更時可能會導致修改遺漏,極大降低代碼的可擴展性和可維護性。

Long Method:長函數

沒有具體的標準判定一個函數內的代碼行數超過哪個具體數值才能算的上“過長”,但是,如果我們去閱讀某個函數的源碼,其代碼行數過多已經造成理解的混亂或障礙,就已經嗅到了函數過長引起的壞味道了。

Large Class:過大的類

單個類具有龐大的代碼片段,擁有眾多的屬性和方法,承擔了過多的事情,一般會違反SRP原則,或者抽象層級表述不清。

Long Parameter List:過長的函數參數列表

參數個數一般不建議超過三個。過多參數導致函數職責難以理解,引起可維護性的下降。如果參數等於或大於三個我們就可以考慮進行適當的重構了。

Divergent Change:發散式變化

如果某個類經常由於不同的原因在不同方向上發生變化,這種壞味道就是“發散式變化”,本質上來看,對於這種類我們要關注其是否遵循了單一職責原則。

Shotgun Surgery:霰彈式修改

這種壞味道就是:當遇到某種變化,需要在代碼中許多不同的地方進行修改。

Feature Envy

對象封裝了數據和行為,如果一個類中的函數對其他類中數據的興趣超過了自己所在的類,例如,某個函數進行運算要頻繁的從獲取其他類中的數據,那麼這種情況就需要考慮函數的位置是否應該從該類中移出到其他類中。

Data Clumps

如果在代碼中常常可以看到在許多地方看到相同的三四項數據,比如勒種有相同的字段,或函數簽名中的相同的參數,那麼這些經常出現的、相同的數據項應該為之分配一個類了。

Primitive Obsession

不論是在類的屬性或方法參數中,你可能更傾向於使用語言自帶的簡單數據類型。但有時候,不應該偏執於使用基本類型,而應該嘗試將這些屬性或參數封裝為更具表現力的類。比如常見的地址信息、錢(數量+貨幣單位)、姓名信息(姓 + 名)等等。

Switch 語句

Switch語句或if/else條件分支的使用具備一定的可讀性和簡潔性,其本身並不具有壞味道。這些語句的使用通常意味著要進行分支決策,問題在於這種同樣的決策邏輯往往會出現在系統的不同地方而非只有一處。當需要擴展時則需要修改代碼分支,且需要多處修改不能遺漏,這就是“壞味道”

Lazy Class

類不應該被過度創建,已經創建的類要具備其應有的職責和價值。代碼中不應該存在這種沒有太多實際價值,且還需要額外維護的類。

Speculative Generality

代碼中規劃了太多的 “TODO”了,但這些“TODO”工作短期內不會做或者後續可能根本不會進行實現,應該避免這些“過度的規劃”。

Temporary Field

如果類中的某個或少數幾個變量都是為了某種特定情況而存在,其實在大多數情況下都不會用到這些屬性。當閱讀代碼時不由自主的猜測這些屬性設立的最初目的,對你來說是一種干擾。

Message Chains

如果在方法a()如果存在類似這樣的代碼:

B b = b.call();

C c = c.call(b);

D d = d.call(c);

......

調用多個方法,又將返回對象作為參數依次調用下一個方法。這種顯式的調用鏈導致了A.a()中的代碼與導航結構緊密耦合,當對象關係發生變化時不得不對A進行代碼修改。

Middle Man

封裝是對象的關鍵特徵之一,將複雜的操作進行封裝,對外屏蔽操作的細節,調用方將實現邏輯委託給其他類或方法實現。但如果你的類中存在過多的方法都是基於這種“委派”就需要引起注意,這是一種 “過度使用” 了。

Inappropriate Intimacy

是指兩個類的關係過於密切,類中的方法過多的關注彼此的數據,導致兩個類間緊密的耦合和交互的錯綜複雜。這種情況下,需要考慮是否應該將方法或屬性進行移動,儘量使其在單個類中內聚。

Alternative Classes with Different Interfaces

這種現象是指:如果兩個函數都做的是同樣的事情,但在代碼中卻有不同的方法簽名。Comments

註釋

本身沒有問題,合理的和必要註釋是有價值的。但問題在於不要出現無效或不合理的註釋,比如:

  1. 不恰當的信息:版本/變更歷史記錄/作者等適合通過配置管理系統維護的不要放在註釋中
  2. 廢棄的註釋:建議刪除廢棄的或過期的註釋
  3. 冗餘的註釋:代碼具有自注釋能力,去除無用的冗餘的註釋

輸出參數:

函數的形參傳遞引用,在函數內部對形參指向的數據直接進行修改,這種隱蔽的修改就是一種壞味道,造成程序的可讀性下降,出現Bug時不利於問題排查。如果確實需要修改,則應該是通過返回值的方式實現。

標識參數

如果函數的參數有明顯的布爾值參數或其他標識操作類型的變量就應該注意了,我們需要考慮函數是否承擔了過多的職責,而非只專注於一件事兒。

死函數、死代碼和註釋掉的代碼

所謂死函數就是永遠不會被調用的函數,死代碼是指永遠不會被執行的代碼,同註釋掉的代碼一樣,這些死函數應該直接在代碼中刪除。

錯誤的抽象層級上的代碼

這種壞味道是非常常見的問題,特別是那些又臭又長的函數,繁雜的代碼中可能做了不同抽象層級需要做的事情。函數中應該只做一件事兒,且只做與函數所在的抽象層級一致的邏輯,不要把過多的下層邏輯代碼都堆砌到上層。PS: 函數的命名就反映了你對函數所要表達的抽象層級。

垂直分割

變量和函數應該在靠近被使用的地方定義,不要相隔太遠。對於被調用的函數而言,要儘量靠近調用方,方面閱讀代碼時,不要移動太多行數就能找到被調用函數(如果在一個類中)。

無處不在的魔數

代碼中經常出現不能字描述的數字或字符串則稱之為魔數,這種硬編碼的數字對於維護來說簡直是噩夢,所以必須要將其聲明為常量後再引用。

代碼本應優雅!識別哪些需要重構的“壞味道”

作為一個開發人員,除了實現功能之外,更為重要的是具備重構意識,經常思考如何才能更優雅的實現,而非機械的堆砌代碼。


分享到:


相關文章: