第6章重新組織函數(shù)_第1頁
第6章重新組織函數(shù)_第2頁
第6章重新組織函數(shù)_第3頁
第6章重新組織函數(shù)_第4頁
第6章重新組織函數(shù)_第5頁
已閱讀5頁,還剩37頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

1、第6章 重新組織函數(shù) 重構(gòu)在多數(shù)情況下是對(duì)函數(shù)進(jìn)行整理,過長函數(shù)是遇到的最多問題。它們包含太多信息,這些信息又被函數(shù)錯(cuò)綜復(fù)雜的邏輯掩蓋,不易鑒別。 常用的解決方法就是提煉函數(shù),把一段代碼從原先函數(shù)中提取出來,放進(jìn)單獨(dú)函數(shù)。6.1 提煉函數(shù)void printOwing(double amount) printBanner();/print detailsSystem.out.println (name: + _name);System.out.println (amount + amount);void printOwing(double amount) printBanner();print

2、Details(amount);void printDetails (double amount) System.out.println (name: + _name);System.out.println (amount + amount);將這段代碼放進(jìn)一個(gè)獨(dú)立函數(shù)中,并讓函數(shù)名稱解釋該函數(shù)的用途。優(yōu)點(diǎn): 增加函數(shù)彼此間復(fù)用的機(jī)會(huì) 使高層函數(shù)碼讀起來象一系列注釋方法: 創(chuàng)造新函數(shù),根據(jù)這個(gè)函數(shù)的用途來給它命名。即使要提煉的代碼非常簡單,例如只是一條消息或一個(gè)函數(shù)調(diào)用,只要新函數(shù)的名稱能夠以更好方式表示代碼意圖,應(yīng)該提煉它。但如果想不出更有意義的名稱,就別動(dòng)。 將提煉出的代碼從源函數(shù)拷貝到新

3、建的目標(biāo)函數(shù)中。 仔細(xì)檢查提煉出的代碼,看看其是否引用了作用域限于源函數(shù)的變量(包括局部變量和源函數(shù)參數(shù))。 檢查是否有僅用于被提煉部分的臨時(shí)變量。如果有,在目標(biāo)函數(shù)中將它們聲明為臨時(shí)變量。 檢查被提煉碼,看看是否有任何局部變量的值被改變。如果臨時(shí)變量值被修改,看是否可以將被提煉碼處理為一個(gè)查詢,并將結(jié)果賦值給相關(guān)變量。 將被提煉代碼中需要讀取的局部變量,當(dāng)做參數(shù)傳給目標(biāo)函數(shù)。 處理完所有局部變量之后,進(jìn)行編譯。 在源函數(shù)中,將被提煉碼替換為對(duì)目標(biāo)函數(shù)的調(diào)用。如果將任何臨時(shí)變量移到目標(biāo)函數(shù)中,檢查它們原本的聲明式是否在被提煉碼的外圍。如果是,刪除這些聲明式。 編譯,測試。范例:無局部變量voi

4、d printOwing() Enumeration e = _orders.elements();double outstanding = 0.0;/ print bannerSystem.out.println (*);System.out.println (* Customer Owes *);System.out.println (*);/ calculate outstandingwhile (e.hasMoreElements() Order each = (Order) e.nextElement();outstanding += each.getAmount();/print

5、detailsSystem.out.println (name: + _name);System.out.println (amount + outstanding);提煉出“打印banner”的代碼。只需要剪切、粘貼、再插入一個(gè)函數(shù)調(diào)用動(dòng)作:void printOwing() Enumeration e = _orders.elements();double outstanding = 0.0;printBanner();/ calculate outstandingwhile (e.hasMoreElements() Order each = (Order) e.nextElement()

6、;outstanding += each.getAmount();/print detailsSystem.out.println (name: + _name);System.out.println (amount + outstanding);void printBanner() / print bannerSystem.out.println (*);System.out.println (* Customer Owes *);System.out.println (*); 有局部變量 包括傳進(jìn)源函數(shù)的參數(shù)和源函數(shù)所聲明的臨時(shí)變量。 如果被提煉碼只是讀取這些變量的值,并不修改它們,那么可

7、簡單地將它們當(dāng)做參數(shù)傳給目標(biāo)函數(shù)。void printOwing() Enumeration e = _orders.elements();double outstanding = 0.0;printBanner();/ calculate outstandingwhile (e.hasMoreElements() Order each = (Order) e.nextElement();outstanding += each.getAmount();/print detailsSystem.out.println (name: + _name);System.out.println (amo

8、unt + outstanding);可以將“打印詳細(xì)信息”這一部分提煉為帶參數(shù)的函數(shù):void printOwing() Enumeration e = _orders.elements();double outstanding = 0.0;printBanner();/ calculate outstandingwhile (e.hasMoreElements() Order each = (Order) e.nextElement();outstanding += each.getAmount();printDetails(outstanding);void printDetails (

9、double outstanding) System.out.println (name: + _name);System.out.println (amount + outstanding);如果局部變量是個(gè)對(duì)象,而被提煉碼調(diào)用了會(huì)對(duì)該對(duì)象造成修改的函數(shù),也可以同樣操作,只需將這個(gè)對(duì)象作為參數(shù)傳遞給目標(biāo)函數(shù)即可。 對(duì)局部變量再賦值 這個(gè)變量只在被提煉碼區(qū)間使用??梢詫⑦@個(gè)臨時(shí)變量的聲明式移到被提煉碼中,然后一起提出去。 被提煉碼之外的代碼也使用了這個(gè)變量。這又分為兩種情況:如果這個(gè)變量在被提煉碼之后未再被使用,只需直接在目標(biāo)函數(shù)中修改它;如果被提煉碼之后的代碼還使用了這個(gè)變量,就需要讓目標(biāo)函

10、數(shù)返回該變量改變后的值。這里討論臨時(shí)變量的問題。被賦值的臨時(shí)變量分兩種情況:void printOwing() Enumeration e = _orders.elements();double outstanding = 0.0;printBanner();/ calculate outstandingwhile (e.hasMoreElements() Order each = (Order) e.nextElement();outstanding += each.getAmount();printDetails(outstanding);void printOwing() printBa

11、nner();double outstanding = getOutstanding();printDetails(outstanding);double getOutstanding() Enumeration e = _orders.elements();double outstanding = 0.0;while (e.hasMoreElements() Order each = (Order) e.nextElement();outstanding += each.getAmount();return outstanding;Enumeration 變量e 只在被提煉碼中用到,所以將它

12、整個(gè)搬到新函數(shù)中。double 變量outstanding 在被提煉碼內(nèi)外都被用到,必須讓提煉出來的新函數(shù)返回它。編譯測試完成后,把回傳值改名,命名原則:double getOutstanding() Enumeration e = _orders.elements();double result = 0.0;while (e.hasMoreElements() Order each = (Order) e.nextElement();result = each.getAmount();return result;outstanding 變量只是很簡單地被初始化為一個(gè)明確初值,所以可以只在新函

13、數(shù)中對(duì)它初始化。如果代碼還對(duì)這個(gè)變量做了其他處理,就必須將它的值作為參數(shù)傳給目標(biāo)函數(shù)。void printOwing(double previousAmount) Enumeration e = _orders.elements();double outstanding = previousAmount * 1.2;printBanner();/ calculate outstandingwhile (e.hasMoreElements() Order each = (Order) e.nextElement();outstanding += each.getAmount();printDet

14、ails(outstanding);void printOwing(double previousAmount) double outstanding = previousAmount * 1.2;printBanner();outstanding = getOutstanding(outstanding);printDetails(outstanding);double getOutstanding(double initialValue) double result = initialValue;Enumeration e = _orders.elements();while (e.has

15、MoreElements() Order each = (Order) e.nextElement();result += each.getAmount();return result;在將變量outstanding的初始化過程整理如下:void printOwing(double previousAmount) printBanner();double outstanding = getOutstanding(previousAmount * 1.2);printDetails(outstanding);6.2 將函數(shù)內(nèi)聯(lián)化在函數(shù)調(diào)用點(diǎn)插入函數(shù)本體,然后移除該函數(shù)。int getRating

16、() return (moreThanFiveLateDeliveries() ? 2 : 1;boolean moreThanFiveLateDeliveries() return _numberOfLateDeliveries 5;int getRating() return (_numberOfLateDeliveries 5) ? 2 : 1; 以簡短的函數(shù)表現(xiàn)動(dòng)作意圖,會(huì)使代碼更清晰易讀。但有時(shí)候會(huì)遇到某些函數(shù),其內(nèi)部代碼和函數(shù)名稱同樣清晰易讀。這時(shí)就應(yīng)該去掉這個(gè)函數(shù),直接使用其中的代碼。間接性可能帶來幫助,但不必要的間接性總是讓人不舒服。 有一群組織不合理的函數(shù),可以將它們都內(nèi)聯(lián)到

17、一個(gè)大型函數(shù)中,再從中提煉出組織合理的小型函數(shù)??梢园阉{(diào)用對(duì)象的函數(shù)內(nèi)容都放到函數(shù)對(duì)象中。比起既要移動(dòng)一個(gè)函數(shù),又要移動(dòng)它所調(diào)用的其他所有函數(shù),將大型函數(shù)作為單一整體來移動(dòng)要簡單得多。 如果別人使用了太多間接層,使得系統(tǒng)所有函數(shù)都似乎只是對(duì)另一個(gè)函數(shù)的簡單委託,造成在這些委託動(dòng)作之間暈頭轉(zhuǎn)向,那么通常都采用內(nèi)聯(lián)方法。方法: 檢查函數(shù),確定它不具多態(tài)性。如果子類繼承了這個(gè)函數(shù),就不要將此函數(shù)內(nèi)聯(lián)化,因?yàn)樽宇悷o法覆寫一個(gè)根本不存在的函數(shù)。 找出這個(gè)函數(shù)的所有被調(diào)用點(diǎn)。 將這個(gè)函數(shù)的所有被調(diào)用點(diǎn)都替換為函數(shù)本體(代碼)。 編譯,測試。 刪除該函數(shù)的定義。6.3 將臨時(shí)變量內(nèi)聯(lián)化將所有對(duì)該變量的引

18、用動(dòng)作,替換為對(duì)它賦值的那個(gè)表達(dá)式自身double basePrice = anOrder.basePrice();return (basePrice 1000)return (anOrder.basePrice() 1000)某個(gè)臨時(shí)變量被賦予某個(gè)函數(shù)調(diào)用的返回值。如果這個(gè)臨時(shí)變量妨礙了其他的重構(gòu)手法,應(yīng)該將它內(nèi)聯(lián)化。方法: 如果這個(gè)臨時(shí)變量并未被聲明為final,那就將它聲明為final,然后編譯。 這可以檢查該臨時(shí)變量是否真的只被賦值次。找到該臨時(shí)變量的所有引用點(diǎn),將它們替換為臨時(shí)變量賦值語句中等號(hào)右側(cè)的表達(dá)式。 每次修改后,編譯并測試。 修改完所有引用點(diǎn)之后,刪除該臨時(shí)變量的聲明式和賦

19、值語句。 編譯,測試。6.4 以查詢?nèi)〈R時(shí)變量 程序以臨時(shí)變量保存某一表達(dá)式的運(yùn)算結(jié)果。將這個(gè)表達(dá)式提煉到獨(dú)立函數(shù)(查詢 query)中。將這個(gè)臨時(shí)變量的所有被引用點(diǎn)替換為對(duì)新函數(shù)的調(diào)用。新函數(shù)可被其他函數(shù)使用。double basePrice = _quantity * _itemPrice;if (basePrice 1000)return basePrice * 0.95;elsereturn basePrice * 0.98;if (basePrice() 1000)return basePrice() * 0.95;elsereturn basePrice() * 0.98;.do

20、uble basePrice() return _quantity * _itemPrice; 找出只被賦值一次的臨時(shí)變量。如果某個(gè)臨時(shí)變量被賦值超過一次,考慮使用將它分割成多個(gè)變量。 將該臨時(shí)變量聲明為final。 編譯。確保該臨時(shí)變量的確只被賦值次。 將 對(duì)臨時(shí)變量賦值語句的等號(hào)右側(cè)部分提煉到獨(dú)立函數(shù)。 首先將函數(shù)聲明為private。確保提煉出來的函數(shù)無任何連帶影響,也就是說該函數(shù)并不修改任何對(duì)象內(nèi)容。如果它有連帶影響,就對(duì)它從實(shí)現(xiàn)中分離出來。 編譯,測試。 在該臨時(shí)變量身上實(shí)施內(nèi)聯(lián)方法:double getPrice() int basePrice = _quantity * _ite

21、mPrice;double discountFactor;if (basePrice 1000) discountFactor = 0.95;else discountFactor = 0.98;return basePrice * discountFactor;先把臨時(shí)變量聲明為final,檢查它們是否的確只被賦一次:double getPrice() final int basePrice = _quantity * _itemPrice;final double discountFactor;if (basePrice 1000) discountFactor = 0.95;else d

22、iscountFactor = 0.98;return basePrice * discountFactor;之所以先做這件事,因?yàn)槿绻R時(shí)變量不只被賦值一次,就不該進(jìn)行這項(xiàng)重構(gòu)。然后開始替換臨時(shí)變量,每次一個(gè)。首先把賦值動(dòng)作的右側(cè)表達(dá)式提煉出來:double getPrice() final int basePrice = basePrice();final double discountFactor;if (basePrice 1000) discountFactor = 0.95;else discountFactor = 0.98;return basePrice * discount

23、Factor;private int basePrice() return _quantity * _itemPrice;編譯并測試。把臨時(shí)變量basePrice 的第一個(gè)引用點(diǎn)替換掉:double getPrice() final int basePrice = basePrice();final double discountFactor;if (basePrice() 1000) discountFactor = 0.95;else discountFactor = 0.98;return basePrice * discountFactor;編譯、測試。由于下一個(gè)變量已經(jīng)是basePr

24、ice的最后一個(gè)引用點(diǎn),所以我把basePrice 臨時(shí)變量的聲明式一并摘除:double getPrice() final double discountFactor;if (basePrice() 1000) discountFactor = 0.95;else discountFactor = 0.98;return basePrice() * discountFactor;再以類似辦法提煉出一個(gè)discountFactor():double getPrice() final double discountFactor = discountFactor();return basePric

25、e() * discountFactor;private double discountFactor() if (basePrice() 1000) return 0.95;else return 0.98;最終,最終,getPrice()形式如下:形式如下:double getPrice() return basePrice() * discountFactor();6.5 引入解釋性變量將該復(fù)雜表達(dá)式(或其中一部分)的結(jié)果放進(jìn)一個(gè)臨時(shí)變量,以此變量名稱來解釋表達(dá)式用途。if ( (platform.toUpperCase().indexOf(MAC) -1) &(browser.t

26、oUpperCase().indexOf(IE) -1) &wasInitialized() & resize 0 )/ do somethingfinal boolean isMacOs = platform.toUpperCase().indexOf(MAC) -1;final boolean isIEBrowser = browser.toUpperCase().indexOf(IE) -1;final boolean wasResized = resize 0;if (isMacOs & isIEBrowser & wasInitialized() &a

27、mp; wasResized) / do something 在條件邏輯中,引入解釋性變量特別有價(jià)值:可以用這項(xiàng)重構(gòu)將每個(gè)條件子句提煉出來,以一個(gè)良好命名的臨時(shí)變量來解釋對(duì)應(yīng)條件子句的意義。 在較長算法中,可以運(yùn)用臨時(shí)變量來解釋每一步運(yùn)算的意義。 實(shí)際中并不常用,多數(shù)用提煉函數(shù)來解釋一段代碼的意義。畢竟臨時(shí)變量只在它所處的那個(gè)函數(shù)中才有意義,局限性較大,函數(shù)則可以在對(duì)象的整個(gè)生命中都有用,并且可被其他對(duì)象使用。但有時(shí)候,當(dāng)局部變量使提煉函數(shù)方法難以進(jìn)行時(shí),就引入解釋性變量。 聲明一個(gè)final 臨時(shí)變量,將待分解之復(fù)雜表達(dá)式中的一部分動(dòng)作的運(yùn)算結(jié)果 賦值給它。 將表達(dá)式中的運(yùn)算結(jié)果這一部分,替

28、換為上述臨時(shí)變量。如果被替換的這部分在代碼中重復(fù)出現(xiàn),可以每次一個(gè),逐一替換。 編譯,測試。 重復(fù)上述過程,處理表達(dá)式的其他部分方法:double price() / price is base price - quantity discount + shippingreturn _quantity * _itemPrice -Math.max(0, _quantity - 500) * _itemPrice * 0.05 +Math.min(_quantity * _itemPrice * 0.1, 100.0);底價(jià)(baseprice)等于數(shù)量(quantity)乘以單價(jià)(item pr

29、ice)。于是把這部分計(jì)算的結(jié)果放進(jìn)一個(gè)臨時(shí)變量:double price() / price is base price - quantity discount + shippingfinal double basePrice = _quantity * _itemPrice;return basePrice -Math.max(0, _quantity - 500) * _itemPrice * 0.05 +Math.min(_quantity * _itemPrice * 0.1, 100.0);稍后也用上了“數(shù)量乘以單價(jià)”運(yùn)算結(jié)果,所以同樣將它替換為basePrice 臨時(shí)變量:dou

30、ble price() / price is base price - quantity discount + shippingfinal double basePrice = _quantity * _itemPrice;return basePrice -Math.max(0, _quantity - 500) * _itemPrice * 0.05 +Math.min(basePrice * 0.1, 100.0);將批發(fā)折扣(quantity discount)的計(jì)算提煉出來,將結(jié)果賦予臨時(shí)變量quantityDiscount:double price() / price is bas

31、e price - quantity discount + shippingfinal double basePrice = _quantity * _itemPrice;final double quantityDiscount = Math.max(0, _quantity - 500) *_itemPrice * 0.05;return basePrice - quantityDiscount +Math.min(basePrice * 0.1, 100.0);最后,再把運(yùn)費(fèi)(shipping)計(jì)算提煉出來,將運(yùn)算結(jié)果賦予臨時(shí)變量shipping。同時(shí)還可以刪掉代碼中的注釋,因?yàn)楝F(xiàn)在代碼

32、已經(jīng)可以完美表達(dá)自己的意義了:double price() final double basePrice = _quantity * _itemPrice;final double quantityDiscount = Math.max(0, _quantity - 500) *_itemPrice * 0.05;final double shipping = Math.min(basePrice * 0.1, 100.0);return basePrice - quantityDiscount + shipping; 運(yùn)用函數(shù)提煉處理上述過程原函數(shù)double price() / price

33、 is base price - quantity discount + shippingreturn _quantity * _itemPrice -Math.max(0, _quantity - 500) * _itemPrice * 0.05 +Math.min(_quantity * _itemPrice * 0.1, 100.0);double price() / price is base price - quantity discount + shippingreturn basePrice() -Math.max(0, _quantity - 500) * _itemPrice

34、 * 0.05 +Math.min(basePrice() * 0.1, 100.0);private double basePrice() return _quantity * _itemPrice;double price() return basePrice() - quantityDiscount() + shipping();private double quantityDiscount() return Math.max(0, _quantity - 500) * _itemPrice * 0.05;private double shipping() return Math.min

35、(basePrice() * 0.1, 100.0);private double basePrice() return _quantity * _itemPrice; 應(yīng)該在什么時(shí)候引入解釋性變量呢?在提煉函數(shù)需要花費(fèi)更大工作量時(shí)。如果要處理的是一個(gè)擁有大量局部變量的算法,那么提煉函數(shù)很困難。這種情況下會(huì)引入解釋性變量幫助理清代碼。搞清楚代碼邏輯之后,可以運(yùn)用查詢替代臨時(shí)變量,把被引入的那些解釋性臨時(shí)變量去掉。6.6 分離臨時(shí)變量程序某個(gè)臨時(shí)變量被賦值超過一次,它既不是循環(huán)變量(loop variable),也不是集用臨時(shí)變量(collecting temporary variable)。針

36、對(duì)每次賦值,創(chuàng)造一個(gè)獨(dú)立的、對(duì)應(yīng)的臨時(shí)變量。double temp = 2 * (_height + _width);System.out.println (temp);temp = _height * _width;System.out.println (temp);final double perimeter = 2 * (_height + _width);System.out.println (perimeter);final double area = _height * _width;System.out.println (area); 臨時(shí)變量有各種不同用途,某些用途會(huì)很自然地導(dǎo)

37、致臨時(shí)變量被多次賦值。 循環(huán)變量和集用臨時(shí)變量就是兩個(gè)典型例子:循環(huán)變量會(huì)隨循環(huán)的每次運(yùn)行而改變(例如for (int i=0; i 0) double primaryVel = acc * _delay; acc = (_primaryForce + _secondaryForce) / _mass;/第二次賦值處第二次賦值處result += primaryVel * secondaryTime + 0.5 * acc * secondaryTime * secondaryTime;return result;首先,在函數(shù)開始處修改這個(gè)臨時(shí)變量的名稱,并將新的臨時(shí)變量聲明為final。然后

38、把第二次賦值之前對(duì)acc 變量的所有引用點(diǎn),全部改用新的臨時(shí)變量。最后,在第二次賦值處重新聲明acc變量:double getDistanceTravelled (int time) double result;final double primaryAcc = _primaryForce / _mass;int primaryTime = Math.min(time, _delay);result = 0.5 * primaryAcc * primaryTime * primaryTime;int secondaryTime = time - _delay;if (secondaryTime

39、 0) double primaryVel = primaryAcc * _delay;double acc = (_primaryForce + _secondaryForce) / _mass;result += primaryVel * secondaryTime + 0.5 * acc * secondaryTime* secondaryTime;return result;新的臨時(shí)變量的名稱指出,它只承擔(dān)原先acc 變量的第一個(gè)責(zé)任。我將它聲明為final,確保它只被賦值一次。然后,在原先acc 變量第二次被賦值處重新聲明acc。繼續(xù)處理acc 臨時(shí)變量的第二次賦值。把原先的臨時(shí)變量

40、完全刪掉,代之以一個(gè)新的臨時(shí)變量。新變量的名稱指出,它只承擔(dān)原先acc 變量的第二個(gè)責(zé)任:double getDistanceTravelled (int time) double result;final double primaryAcc = _primaryForce / _mass;int primaryTime = Math.min(time, _delay);result = 0.5 * primaryAcc * primaryTime * primaryTime;int secondaryTime = time - _delay;if (secondaryTime 0) double primaryVel = primaryAcc * _delay;final double secondaryAcc = (_primaryForce + _secondaryForce)/ _mass;result += primar

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論