重構筆記——引入解釋性變數
阿新 • • 發佈:2019-01-27
在上一篇文章中介紹了“以查詢取代臨時變數“。本文將介紹“引入解釋性變數”這種重構手法。
下面讓我們來學習這種重構手法吧。
開門見山
發現:你有一個複雜的表示式。
解決:將該複雜的表示式(或其中的部分)的結果放進一個臨時變數,並以此變數名稱來解釋表示式用途。
//重構前 if((platform.toUpperCase().indexOf("MAC") > -1) && (browser.toUpperCase().indexOf("IE") > -1) && wasInitialized() && resize > 0) { //do something }
//重構後
final boolean isMacOs = platform.toUpperCase().indexOf("MAC") > -1;
final boolean isIEBrowser = browser.toUpperCase().indexOf("IE") > -1;
final boolean wasResize = resize > 0;
if(isMacOs && isIEBrowser && wasInitialized() && wasResize){
//do something
}
動機
在某些情況下,表示式可能非常的複雜以至於難以閱讀。這樣,臨時變數可以幫助你將表示式分解為比較容易管理的形式。
在條件邏輯中,引入解釋性變數就顯得比較有價值:你可以用這項重構將每個子句提煉出來,以一個良好命名的臨時變數來解釋對應條件子句的意義。另一種可能的情況是,對於那些比較長的演算法,可以運用臨時變數來解釋每一步運算的意義。
本文的重構手法是比較常見的手法之一,但是對其的使用又不是那麼的多。因為一般情況下,我們都可以使用提煉函式來解釋一段程式碼的意義。畢竟臨時變數只有在它所處的那個函式中才有意義,侷限性較大,函式則可以在物件的整個生命週期中都有用,並且可被其它物件使用。但是,當局部變數使用提煉函式難以進行時,就可以嘗試使用引入解釋性變數。
做法
(1)宣告一個final型的臨時變數,將待分解之複雜表示式中的一部分動作的運算結果賦值給它。 (2)將表示式中的“運算結果”這一部分,替換為上述的臨時變數。(如果被替換的這一部分在程式碼中重複出現,可以每次一個,逐一進行替換) (3)編譯,測試。 (4)重複上述過程,處理其它類似部分。示例
我們從一個簡單計算開始: //重構前
double price(){
// 價格 = basePrice - quantity discount + shipping
return _quantity * _itemPrice -
Math.max(0, _quantity - 800) * _itemPrice * 0.15 +
Math.min(_quantity * _itemPrice * 0.25, 100);
}
這段程式碼還是比較簡單,不過現在要讓其更加容易理解一些。
首先發現底價(basePrice)等於數量(quantity)乘以單價(item price)。於是可以把這一部分的計算結果放進一個臨時變數中,同時將Math.min()函式中引數進行同樣替換。
double price(){
// 價格 = basePrice - quantity discount + shipping
final double basePrice = _quantity * _itemPrice;
return basePrice -
Math.max(0, _quantity - 800) * _itemPrice * 0.15 +
Math.min(basePrice * 0.25, 100);
}
然後,將批發折扣(quantity discount)的計算提煉出來,並將運算結果賦予臨時變數。
double price(){
// 價格 = basePrice - quantity discount + shipping
final double basePrice = _quantity * _itemPrice;
final double quantityDiscount = Math.max(0, _quantity - 800) * _itemPrice * 0.15;
return basePrice -quantityDiscount+
Math.min(basePrice * 0.25, 100);
}
最後,再把搬運費(shipping)計算提煉出來,並將運算結果賦予臨時變數。
//重構後
double price(){
// 價格 = basePrice - quantity discount + shipping
final double basePrice = _quantity * _itemPrice;
final double quantityDiscount = Math.max(0, _quantity - 800) * _itemPrice * 0.15;
final double shipping = Math.min(basePrice * 0.25, 100);
return basePrice - quantityDiscount + shipping;
}
運用提煉函式處理
對於上述程式碼,通常不以臨時變數來解釋其動作意圖,而是更喜歡使用提煉函式。 讓我們從頭開始: //重構前
double price(){
// 價格 = basePrice - quantity discount + shipping
return _quantity * _itemPrice -
Math.max(0, _quantity - 800) * _itemPrice * 0.15 +
Math.min(_quantity * _itemPrice * 0.25, 100);
}
現在把底價計算提煉到一個獨立的函式中。
double price(){
// 價格 = basePrice - quantity discount + shipping
return basePrice() -
Math.max(0, _quantity - 800) * _itemPrice * 0.15 +
Math.min(basePrice() * 0.25, 100);
}
private double basePrice(){
return _quantity * _itemPrice;
}
繼續進行提煉,每次提煉一個新的函式。最後得到程式碼如下。 //重構後
double price(){
// 價格 = basePrice - quantity discount + shipping
return basePrice() - quantityDiscount() + shipping();
}
private double basePrice(){
return _quantity * _itemPrice;
}
private double shipping(){
return Math.min(basePrice() * 0.25, 100);
}
private double quantityDiscount(){
return Math.max(0, _quantity - 800) * _itemPrice * 0.15;
}
本文主要介紹了重構手法——引入解釋性變數。該重構方法主要是在提煉函式需要花費更大工作量時才使用。比如你有一個擁有大量區域性變數的演算法,那麼使用提煉函式絕非易事。這時候就可以使用本文的方法來整理程式碼,然後再考慮下一步怎麼辦;一旦搞清楚程式碼邏輯後,就可以運用以查詢取代臨時變數把中間引入的那些臨時變數去掉。
我想你會比較喜歡提煉函式,因為對於同一物件的任何部分,都可以根據自己的需要取用這些提煉出來的函式。一開始會把這些新函式宣告為private;如果其它物件也需要它們,就可以輕易釋放這些函式的訪問控制。最後,希望本文對你有所幫助。有問題可以留言,謝謝。(PS:下一篇將介紹重構筆記——分解臨時變數)