1. 程式人生 > >重構筆記——引入解釋性變數

重構筆記——引入解釋性變數

 在上一篇文章中介紹了“以查詢取代臨時變數“。本文將介紹“引入解釋性變數”這種重構手法。

  下面讓我們來學習這種重構手法吧。

開門見山

發現:你有一個複雜的表示式。

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

	//重構前
	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:下一篇將介紹重構筆記——分解臨時變數)

重構筆記文章如下