1. 程式人生 > >重構——程式碼壞味道&重組函式.md

重構——程式碼壞味道&重組函式.md

程式碼的壞味道

Duplicated Code ( 重複程式碼 )

如果你在一個以上的地點看到相同的程式結構,那麼設法將它們合而唯一,程式會變得更好

最單純的Duplicated Code 是同一個類的兩個函式含有相同的表示式

另一種常見的情況分就是兩個互為兄弟的子類內含相同的表示式

Long Method ( 過長函式 )

每當感覺需要以註釋來說明點什麼的時候,我們就把需要說明的東西寫進一個獨立函式中,並以其用途命名(而非實現手法)。

如何確定該提煉哪一段程式碼呢?一個很好的技巧是:尋找註釋。他們通常能指出程式碼用途和實現手法之間的語義距離。如果程式碼前方有一行註釋,就是再提醒你:可以將這段程式碼換成一個函式,而且可以再註釋的基礎上給這個函式命名。就算只有一行程式碼,如果它需要以註釋來說明,那也值得將它提煉到獨立函式中。

Large Class ( 過大的類 )

Long Parameter List ( 過長引數列 )

通過以物件的成員變數,避免一個函式中引數過長。

Divergent Change ( 發散式變換 )

針對某一外界變化的所有相應修改,都只應該發生在單一類中,而這個新類內的所有內容都應該反應此變化。(介面卡模式)

Shotgun Surgery ( 散彈式修改 )

如果每遇到某種變化,你都必須在許多不同的類內做出許多小修改。

這種情況應該把所有需要修改的程式碼放到同一個類中。

Divergent Change 是指 一個類受多種變化的影響,Shotgun Surgery 是指一個變化引發多個類相應的修改

Feature Envy ( 依賴情結 )

函式對某個類的興趣高過對自己所處類的興趣。例如某個函式為了計算某個值,從另一個物件那呼叫幾乎半打的取值函式。

判斷哪個類擁有最多被彼此函式使用的資料,然後就把這個函式和那些資料擺在一起。

Data Clumps ( 資料泥團 )

兩個類中相同的欄位,許多函式簽名中引數相同

Primitive Obsession ( 基本型別偏執 )

通過一些基本型別的組裝,拼裝出一種小的資料型別。例如java中的String,Date等。

Switch Statements

面向物件程式的一個最明顯的特徵就是:少用switch語句。通過多型的概念來解決。

Paraller Inheritance Hierarchies (平行繼承體系)

每當你為某個類增加一個子類,必須為另一個類相應增加一個子類。

Lazy Class

如果一個類的所得不值其身價,他就應該小時。

Temporary Field ( 令人迷惑的暫時欄位 )

其內某個例項變數僅為某種特定情況而設定。

重構技巧

Extract Method ( 提煉函式 )

動機

當看見一個過長的函式或者一段需要註釋才能讓人理解用途的程式碼,我就會將這段程式碼放進一個獨立函式中。

如果每個函式的粒度都很小,那麼函式被複用的機會就更大。其次,這會使高層函式讀起來就想一系列註釋。再次,函式的複寫也會更容易些。

Inline Method ( 行內函數 )

一個函式的本體與名稱同樣清楚一動

在函式的呼叫點插入函式本體,然後移除該函式。

Inline Temp ( 內聯臨時變數 )

你有一個臨時變數,只被一個簡單表示式賦值一次,而它妨礙了其他重構手法。

將所有對該變數的引用動作,替換為對它賦值的那個表示式自身

	double basePrice = anOrder.basePrice();
	return (basePrice > 10000);

to


return (anOrder.basePrice()) > 1000);

Replace Temp with Query ( 以查詢取代臨時變數 )

你的程式以一個臨時變數儲存某一表達式的運算結果

將這個表示式提煉到一個獨立函式中。將這個臨時變數的所有引用點替換為新函式的呼叫。此後,新函式就可能被其他函式使用

double basePrice = _quantity * _itemPrice;
if (basePrice > 10000) 
	return basePrice * 0.95;
else 
	return basePrice 8 0.98;

to

if (basePrice() > 10000)
	return basePrice() * 0.95;
else 
	return basePrice() * 0.98;

double basePrice() {
	return _quantity * _itemPrice;
}

動機

臨時變數的問題在於:它們是暫時的,而且只能在所屬函式內使用。由於臨時變數只在所屬函式內可見,它們會驅使你寫出更長的函式,因為只有這樣你才能訪問到需要的臨時變數。如果把臨時變數替換為一個查詢,那麼同一個類中所有函式都可以獲得這份資訊。

Introduce Explaining Variable ( 引入解釋性變數 )

將複雜表示式的結果放進一個臨時變數,以此變數名稱來解釋表示式用途。

if ((plathform.toUpperCase().indexOf("Mac") > -1)) {

}

to

final boolean isMacOs = plathform.toUpperCase().index("Mac") > -1;

Split Temporary Variable ( 分解臨時變數 )

你的程式有某個臨時變數被賦值超過一次,它既不是迴圈變數,也不被用於收集計算結果。

針對每次賦值,創造一個獨立、對應的臨時變數

動機

如果臨時變數承擔多個責任,它就應該被替換為多個臨時變數,每個變數只承擔一個責任。

Remove Assignments to Parameters ( 移除對引數的賦值 )

函式內對一個方法的引數進行賦值

以一個臨時變數取代該引數的賦值

Replace Method with Method Object ( 以函式物件取代函式 )

將這個函式放進一個單獨物件中,如此一來區域性變數就成了物件內的欄位。然後再這個物件中將大型函式分解為多個小型函式

Subsititute Algorithm ( 替換演算法 )

換一種函式內邏輯寫法。。。。