重構-改善既有程式碼的設計(六)–重新組織函式

木木甫發表於2019-01-19

函式重構

重構有很大一部分都是在重構函式。尤其是長函式。這是問題的根源。以下是重構方法

Extract Method 提煉函式

提煉函式:(由複雜的函式提煉出獨立的函式或者說大函式分解成由小函式組成)你有一段程式碼可以被組織在一起並獨立出來。將這段程式碼放進一個獨立函式,並讓函式名稱解釋該函式的用途。

重構前

void printOwing() {   
    //print banner  
    System.out.println(“*********”);  
    System.out.println(“Banner”);  
    System.out.println(“*********”);  
    //print details  
    System.out.println ("name: " + _name);   
        System.out.println ("amount " + getOutstanding());   
}  

重構後

                                           
void printOwing()  {  
    printBanner();   
    printDetails(getOutstanding());   
}   
  
Void printBanner()  {  
    //print banner  
    System.out.println(“*********”);  
    System.out.println(“Banner”);  
    System.out.println(“*********”);  
}  
void printDetails (double outstanding)   {   
    System.out.println ("name: " + _name);   
    System.out.println ("amount " + outstanding);   
}

Inline Method 行內函數

行內函數:(直接使用函式體代替函式呼叫 ) 一個函式呼叫的本體與名稱同樣清楚易懂。在函式呼叫點插入函式體,然後移除該函式
重構前

int getRating() {  
    return moreThanfiveLateDeliverise() ? 2 : 1;  
}  
bool moreThanfiveLateDeliverise() {  
    return _numberOfLateLiveries > 5;  
}  

                                                       

重構後

int getRating(){  
     return _numberOfLateLiveries > 5 ? 2 : 1;  
}  

Inline Temp 內聯臨時變數

內聯臨時變數:(表示式代替臨時變數)你有一個臨時變數,只被一個簡單表示式賦值一次,而它妨礙了其他重構手法。將所有對該變數的引用動作,替換為對它賦值的那個表示式自身
重構前

double basePrice = anOrder.BasePrice();  
return basePrice(>1000);  
     

                                                  

重構後

return (anOrder.BasePrice() >1000);

Replace Temp with Query 以查詢代替臨時變數

以查詢代替臨時變數:(獨立函式代替表示式)你的程式以一個臨時變數儲存某一個表示式的運算效果。將這個表示式提煉到一個獨立函式中。將這個臨時變數的所有引用點替換為對新函式的呼叫。此後,新函式就可以被其他函式呼叫。

重構前

double basePrice = _quantity*_itemPrice;  
if (basePrice > 1000) {  
    return basePrice * 0.95;  
else   
    return basePrice * 0.98;  
                                                         

重構後

if (basePrice() > 1000)  
    return basePrice() * 0.95;   
else   
    return basePrice() * 0.98;   
……  
double basePrice() {   
    return _quantity * _itemPrice;   
}    

注:這一條我始終不覺得合理。如果從效能上看。明顯重構前的效能會比第二種更好。而且更容易理解

Introduce Explaining Variable 引入解釋性變數

引入解釋性變數:(複雜表示式分解為臨時解釋性變數)你有一個複雜的表示式。將該複雜表示式(或其中一部分)的結果放進一個臨時變數,以此變數名稱來解釋表示式用途。
重構前

if (Platform.ToUpperCass().indexOf("MAC") > -1 && (Browser.ToUpperCass().indexOf("Ie") > -1) && WasInitalized() ) {  
    //do something  
 }  

重構後
const bool imMacOs = Platform.ToUpperCass().indexOf(“MAC”) > -1;
const bool isIeBrowser = Browser.ToUpperCass().indexOf(“Ie”) > -1;
const bool wasInitalized = WasInitalized();
if (imMacOs && isIeBrowser && wasInitalized)
{

//do something  

}
注:這一條和上上條並沒有衝突。上上條指的是單純地取值函式,如get方法。這一條針對無法理解的方法鏈

Split Temporary Variable 分解臨時變數

分解臨時變數:(臨時變數不應該被賦值超過一次)你的程式有某個臨時變數被賦值超過一次,它既不是迴圈變數,也不被用於收集計算結果。針對每次賦值,創造一個獨立、對應的臨時變數
重構前

double temp = 2 + (_height + _width);  
Console.WriteLine(temp);  
temp = _height * _width;  
Console.WriteLine(temp);  

                                                        

重構後

const double perimeter = 2 + (_height + _width);  
Console.WriteLine(perimeter);  
const double area = _height * _width;  
Console.WriteLine(area);

 

Remove Assigments to Parameters 移除對引數的賦值

移除對引數的賦值:(不要對引數賦值)程式碼對一個 引數賦值。以一個臨時變數取代該引數的位置。
重構前

int discount (int inputVal, int quantity, int yearToDate){   
    if (inputVal > 50) inputVal -= 2;   
}  
     

重構後

int discount (int inputVal, int quantity, int yearToDate) {   
     int result = inputVal;   
     if (inputVal > 50) result -= 2;   
}  

注:如果傳的是物件。除非你想要操作物件。否則就留下了bug的風險。因為物件傳入方法被改變了。(這條也要具體情況具體使用)

Replace Method with Method object 函式物件取代函式

函式物件代替函式:(大函式變成類)你有一個大型函式,其中對區域性變數的使用使你無法採用 Extract Method (提煉函式)。將這個大型函式放進一個單獨物件中,如此一來區域性變數就成了物件內的欄位。然後你可以在同一個物件中將這個大型函式分解為多個小型函式。
重構前

class Order...  
double price() {   
    double primaryBasePrice;  
    double secondaryBasePrice;   
    double tertiaryBasePrice;   
    // long computation; ...   
}   

重構後

 class Price {   
        double primaryBasePrice;  
        double secondaryBasePrice;   
        double tertiaryBasePrice;   
        public void price(){
        // long computation; ...   
   

或者可以採用static method

Substitute Algorithm 替換演算法

替換演算法:(函式本體替換為另一個演算法)你想要把某個演算法替換為另一個更清晰地演算法。將函式本體替換為另一個演算法。
重構前

String foundPerson(String[] people){   
    for (int i = 0; i < people.length; i++)  {   
        if (people[i].equals ("Don")){  
             return "Don";   
         }  
         if (people[i].equals ("John")) {  
           return "John";   
         }   
         if (people[i].equals ("Kent")){   
                return "Kent";   
         }   
     }  
     return "";   
}
                                            

重構後

String foundPerson(String[] people){   
    List candidates   
             = Arrays.asList(new String[] {"Don",   "John", "Kent"});   
    for (int i=0; i<people.length; i++)   
        if (candidates.contains(people[i]))   
             return people[i]; return "";   
} 

注:如果可以使用更簡單並清晰的方式。就果斷換方式

相關文章