從新組織函數

《重構-改善既有代碼的設計》 之 「從新組織函數」 筆記java

總共羅列了9種方法。算法

1.Extract Method (提煉函數)函數

將一段代碼放到一個獨立的函數中,並讓函數名稱解釋該函數的用途。spa

--------------------------------------------------------------------設計

無局部變量:直接提煉出來,放到目標函數中。對象

--------------------------------------------------------------------blog

有局部變量(包括原函數參數、原函數中臨時變量這裏只分析臨時變量。排序

Situation 1:被提煉代碼段 「只讀,不修改局部變量。繼承

Solution:局部變量 做爲參數 傳給目標函數。it

 

Situation 2:被提煉代碼段,對局部變量賦值

Ps:這個要分狀況討論。

Situation 2.1:該局部變量 只在被提煉代碼段 使用。

Solution for 2.1:將該局部變量的聲明 移到被提煉代碼段中,一塊兒提煉出來。

 

Situation 2.2:該局部變量 被提煉代碼段 還被使用到。

Ps:兩種狀況。

Situation 2.2.1:該變量 被提煉代碼段 未被使用。

Solution for 2.2.1:直接在目標函數中修改該變量(變量可傳參進來)

 

Situation 2.2.2:該變量 被提煉代碼段 使用。

Solution for 2.2.2:目標函數 須要一個返回值,返回該變量被修改後的值。

 

Ps:若是 被提煉代碼段 對該變量作了其餘操做(不僅僅是明確初始化爲0什麼的),那就必須將該變量做爲參數傳給目標函數。

--------------------------------------------------------------------

2.Inline Method(內聯函數)

解釋:一個函數的 本體名稱 一樣清楚易懂的狀況下,適用該方法。

用法:在函數調用點直接插入函數本體。

Situation 1:即 「適用於」所述。

Solution:函數本體 替換 函數調用處。

 

Situation 2:函數不合理(包括內部結構、函數間調用複雜)

Solution:先將不合理的函數內聯到一個大型函數中,再從中提煉合理的小型函數。

Ps

(a)這種狀況下,內聯函數只是做爲輔助手段使用。

(b)若是子類繼承了這個函數,那父類中就不能內聯該函數了。

 

3.Inline Temp(內聯臨時變量)

解釋:將全部對該變量的引用操做,替換爲對它賦值的那個表達式

Ps:若是該臨時變量妨礙了重構,能夠用Inline Temp方法內聯化,再使用其餘重構方法。

這個方法通常做爲Replace Temp With Query(以查詢取代臨時變量)的一部分使用。

-----------------------------------------------------------------------

舉個例子:某個臨時變量被賦值爲某個函數調用的返回值。

int a = func();
if (a > 0) {// do something}

通常爲了檢查這個臨時變量是否只被賦值一次,能夠將該變量聲明爲final,編譯下。

final int a = func();
//而後沒問題的話,再根據這個方法修改成:
if (func() > 0) {// do something}

-----------------------------------------------------------------------

4.Replace Temp With Query(以查詢取代臨時變量)

解釋:程序中用一個臨時變量保存某一表達式的運算結果。

作法:將這個表達式提煉到一個獨立函數中,而後用這個函數替換全部對臨時變量的引用。

Ps:固然,這種重構方法較爲簡單的狀況是:臨時變量只被賦值一次或者賦值給臨時變量的表達式不受其餘條件影響。其餘狀況可能就須要先用其餘重構方法處理下,而後再替換臨時變量。

 

例子的話參考Inline Temp中的例子。其實Replace Temp With Query方法包含Inline Temp方法:

Inline Temp方法就是 一個替換的過程;Replace Temp With Query方法是提煉替換的過程。

 

 

5.Introduce Explaining Variable (引入解釋型變量)

解釋:將複雜表達式(或其中一部分)的結果放進一個臨時變量,以此變量名稱來解釋表達式用途。

例子: 

int a = 1, b = 2, c = 3;
if ( (a & b) > 0 && (b | c) != 3 || (-c & a) < 2) {// do something}

//修改成
boolean isConditionA = ((a & b) > 0);
boolean isConditionB = ((b | c) != 3);
boolean isConditionC = ((-c & a) < 2);

if ( isConditionA && isConditionB || isConditionC ) {//do something}

Ps:不過臨時變量的侷限性比較大,而函數複用性高,通常建議仍是先用Extract Method處理。若是Extract Method方法要處理一個大量局部變量的算法,那麼就能夠用本方法處理,而後再想下一步。

 

6.Split Temporary Variable(去除臨時變量)

前提:程序中某個變量被賦值超過一次,它既不是循環變量,也不用於收集計算結果。

作法:針對每次賦值,建立一個獨立、對應的臨時變量。

 

除了循環變量和收集結果的變量外,每一個局部變量都應該只承擔一個責任。同個變量承擔的責任多了,會讓閱讀者感受糊塗。

例子:

int func(int first, int last) {
    int base = first + last;
    int sum = base * (last - first + 1); //第一次賦值
    int halfSum = sum / 2;
    sum = halfSum;  //第二次賦值
    return sum;
}

思路:

針對某一個被賦值超過屢次的變量,首先在其第一次被賦值處修改變量名,並定義爲final;而後把第二次賦值前對sum變量的引用所有修改成對新變量firstSum的引用。以下:

int func(int first, int last) {
    int base = first + last;
    final int firstSum = base * (last - first + 1); //第一次賦值
    int temp = firstSum + 1; //這裏表示其餘操做
    sum = temp / 2;  //第二次賦值
    return sum;
}

而後同理,在第二次賦值處修改變量名,及定義爲final。同上述處理。

int func(int first, int last) {
    int base = first + last;
    final int firstSum = base * (last - first + 1); //第一次賦值
    int temp = firstSum + 1;
    final int secondSum = temp / 2;  //第二次賦值
    return secondSum;
}

這樣就能想到不少其餘重構方法來處理了。

 

7.Remove Assignment to Parameters(移除對參數的賦值)

前提:代碼一個參數 賦值

作法:以一個臨時變量取代該參數的位置。例外:要使用「出參」。

爲何要這麼作:若是直接對參數進行處理,會下降代碼的清晰度,而且可能會混淆按值傳遞和按址傳遞。

 

8.Replace Method with Method Object(以函數對象代替函數)

前提:目前有一個大型函數,其中對局部變量的使用使你沒法採用Extract Method

作法:將這個函數放到一個單獨對象中,這樣局部變量就成了對象內字段了。而後能夠在同一個對象內分解該大型函數。

 

主要步驟:

1.建立新類A(命名最好跟要處理的大型函數有關)

2.在A類中創建一個final字段,用來保存原先大型函數所在的對象(記爲obj)

針對原函數的每一個臨時變量及參數,建立對應的字段保存。

3.建立構造函數,接收obj對象和原函數的全部參數做爲參數。

4.建立一個方法(compute),複製原函數。若是須要調用原來對象的方法,就經過構造函數中傳進來的obj對象來調用。

5.替換原函數。

這樣就能夠輕鬆的使用Extract Method方法了。

 

9.Substitute Algorithm(替換算法)

前提:你想把某個算法替換爲另外一個更清晰的算法。

作法:將函數本體替換爲另外一個算法。

例子:

修改前

修改後

int sort() {

冒泡排序;

}

int sort() {

歸併排序;

}

 

 

 

 

 

 

 

Over~~

相關文章
相關標籤/搜索