日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

重构-改善既有代码的设计:重新组织函数的九种方法(四)

發(fā)布時(shí)間:2025/6/15 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 重构-改善既有代码的设计:重新组织函数的九种方法(四) 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

??函數(shù)過(guò)長(zhǎng)或者邏輯太混亂,重新組織和整理函數(shù)的代碼,使之更合理進(jìn)行封裝。


1.?Extract Method 提煉函數(shù)

提煉函數(shù):(由復(fù)雜的函數(shù)提煉出獨(dú)立的函數(shù)或者說(shuō)大函數(shù)分解成由小函數(shù)組成) 你有一段代碼可以被組織在一起并獨(dú)立出來(lái)。 將這段代碼放進(jìn)一個(gè)獨(dú)立函數(shù),并讓函數(shù)名稱(chēng)解釋該函數(shù)的用途

  • 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(“*********”);??
  • }??
  • <strong>void?printDetails?(double?outstanding)???{???
  • ????System.out.println?("name:?"?+?_name);???
  • ????System.out.println?("amount?"?+?outstanding);???
  • }?</strong>??
  • 過(guò)長(zhǎng)的函數(shù)或者一段需要注釋才能讓人理解用途的代碼,就應(yīng)該將這段代碼放進(jìn)一個(gè)獨(dú)立函數(shù)中。簡(jiǎn)短而命名良好的函數(shù)的好處: 1)如果每個(gè)函數(shù)的粒度都很小,那么函數(shù)被復(fù)用的機(jī)會(huì)就更大; 2)這會(huì)使高層函數(shù)讀起來(lái)就想一系列注釋; 3)如果函數(shù)都是細(xì)粒度,那么函數(shù)的覆寫(xiě)也會(huì)更容易些。
    一個(gè)函數(shù)多長(zhǎng)才算合適?長(zhǎng)度不是問(wèn)題,關(guān)鍵在于函數(shù)名稱(chēng)和函數(shù)本體之間的語(yǔ)義距離。如果提煉可以強(qiáng)化代碼的清晰度,那就去做,就算函數(shù)名稱(chēng)必提煉出來(lái)的代碼還長(zhǎng)也無(wú)所謂。

    2.?Inline Method 內(nèi)聯(lián)函數(shù)

    內(nèi)聯(lián)函數(shù):(直接使用函數(shù)體代替函數(shù)調(diào)用 )?一個(gè)函數(shù)調(diào)用的本體與名稱(chēng)同樣清楚易懂。在函數(shù)調(diào)用點(diǎn)插入函數(shù)體,然后移除該函數(shù)

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

  • ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

  • int?getRating(){??
  • ?????return?_numberOfLateLiveries?>?5???2?:?1;??
  • }??
  • ? ? ?有時(shí)候你會(huì)遇到某些函數(shù),其內(nèi)部代碼和函數(shù)名稱(chēng)同樣清晰易讀。也可能你重構(gòu)了該函數(shù),使得其內(nèi)容和其名稱(chēng)變得同樣清晰。果真如此,你應(yīng)該去掉這個(gè)函數(shù),直接使用其中的代碼。間接性可能帶來(lái)幫助,但非必要的間接性總是讓人不舒服。
    ? ? ?另一種需要使用Inline Method (內(nèi)聯(lián)函數(shù))的情況是:你手上有一群不甚合理的函數(shù)。你可以將它們都內(nèi)聯(lián)到一個(gè)大型函數(shù)中,再?gòu)闹刑釤挸龊侠淼男『瘮?shù)。實(shí)施Replace Method with Method Object (以函數(shù)對(duì)象取代函數(shù))之前這么做,往往可以獲得不錯(cuò)的效果。你可以把所要的函數(shù)的所有調(diào)用對(duì)象的函數(shù)內(nèi)容都內(nèi)聯(lián)到函數(shù)對(duì)象中。比起既要移動(dòng)一個(gè)函數(shù),又要移動(dòng)它所調(diào)用的其他所有函數(shù),將整個(gè)大型函數(shù)作為整體來(lái)移動(dòng)比較簡(jiǎn)單。
    ? ? 如果別人使用了太多間接層,使得系統(tǒng)中所有函數(shù)都似乎只是對(duì)另一個(gè)函數(shù)的簡(jiǎn)單委托,造成在這些委托動(dòng)作之間暈頭轉(zhuǎn)向,那么就使用 Inline Method (內(nèi)聯(lián)函數(shù))。
    當(dāng)然,間接層有其價(jià)值,但不是所有間接層都有價(jià)值。試著使用內(nèi)聯(lián)手法,可以找出那些有用的間接層,同時(shí)將那些無(wú)用的間接層去除。


    3.Inline Temp 內(nèi)聯(lián)臨時(shí)變量

    內(nèi)聯(lián)臨時(shí)變量:(表達(dá)式代替臨時(shí)變量)你有一個(gè)臨時(shí)變量,只被一個(gè)簡(jiǎn)單表達(dá)式賦值一次,而它妨礙了其他重構(gòu)手法。將所有對(duì)該變量的引用動(dòng)作,替換為對(duì)它賦值的那個(gè)表達(dá)式自身? ? ?
  • double?basePrice?=?anOrder.BasePrice();??
  • return?basePrice(>1000);??
  • ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
    ? ? ? ? ? ? ? ? ? ? ?
  • return?(anOrder.BasePrice()?>1000);??
  • ? ? ? ? Inline Temp(內(nèi)聯(lián)臨時(shí)變量)多半是作為Replace Temp with Query(以查詢?nèi)〈R時(shí)變量)的一部分使用的,所以真正的動(dòng)機(jī)出現(xiàn)在后者那里。唯一單獨(dú)使用Inline Temp(內(nèi)聯(lián)臨時(shí)變量)情況是:你發(fā)現(xiàn)某個(gè)臨時(shí)變量被賦予某個(gè)函數(shù)調(diào)用的返回值
    ? ? ? ?一般來(lái)說(shuō),這樣的臨時(shí)變量不會(huì)有任何危害,可以放心把它留在那。但如果這個(gè)臨時(shí)變量妨礙了其他的重構(gòu)手法,例如 Extract Method (提煉函數(shù)),就應(yīng)該將它內(nèi)聯(lián)化。


    4.Replace Temp with Query 以查詢代替臨時(shí)變量

    以查詢代替臨時(shí)變量:(獨(dú)立函數(shù)代替表達(dá)式)你的程序以一個(gè)臨時(shí)變量保存某一個(gè)表達(dá)式的運(yùn)算效果。將這個(gè)表達(dá)式提煉到一個(gè)獨(dú)立函數(shù)中。將這個(gè)臨時(shí)變量的所有引用點(diǎn)替換為對(duì)新函數(shù)的調(diào)用。此后,新函數(shù)就可以被其他函數(shù)調(diào)用。

  • 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;???
  • }???
  • ? ? 臨時(shí)變量的問(wèn)題在于:它們是暫時(shí)的,而且只能在所屬函數(shù)內(nèi)使用。由于臨時(shí)變量只是在所屬函數(shù)內(nèi)可見(jiàn),所以它們會(huì)驅(qū)使你寫(xiě)出更長(zhǎng)的函數(shù),因?yàn)橹挥羞@樣你才能訪問(wèn)到需要的臨時(shí)變量。如果把臨時(shí)變量替換為一個(gè)查詢,那么同一個(gè)類(lèi)中的所有函數(shù)都可以獲得這份信息。這將帶給你極大幫助,使你能夠?yàn)檫@個(gè)類(lèi)編寫(xiě)更清晰地代碼。
    ? ?? 以查詢代替臨時(shí)變量 往往是你運(yùn)用Extract Method( 提煉函數(shù)) 之前必不可少的一個(gè)步驟。局部變量會(huì)使代碼難以被提煉,所以你應(yīng)該盡可能把它們替換為查詢式。
    ? ? 這個(gè)重構(gòu)手法較為簡(jiǎn)單的情況是:臨時(shí)變量只被賦值一次,或者賦值給臨時(shí)變量的表達(dá)式不受其他條件影響。其他情況比較棘手,但也可能發(fā)生。你可能需要先運(yùn)用Split Temporary Variable(分解臨時(shí)變量)或Separate Query form Modifier(將查詢函數(shù)和修改函數(shù)分離) 使情況變得簡(jiǎn)單一些,然后再替換臨時(shí)變量。如果你想替換的臨時(shí)變量是用來(lái)收集結(jié)果的)例如循環(huán)中的累加值),就需要將某些程序邏輯(例如循環(huán))復(fù)制到查詢函數(shù)去。


    5.Introduce Explaining Variable 引入解釋性變量

    引入解釋性變量:(復(fù)雜表達(dá)式分解為臨時(shí)解釋性變量)你有一個(gè)復(fù)雜的表達(dá)式。將該復(fù)雜表達(dá)式(或其中一部分)的結(jié)果放進(jìn)一個(gè)臨時(shí)變量,以此變量名稱(chēng)來(lái)解釋表達(dá)式用途。
  • 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??
  • }??
  • ? ? 表達(dá)式有可能非常復(fù)雜而難以閱讀。這種情況下,臨時(shí)變量可以幫助你將表達(dá)式分解為比較容易管理的形式。
    ? ? 在條件邏輯中,Introduce Explaining Variable(引入解釋性變量)是一個(gè)很常見(jiàn)的手法,但是最好盡量使用?Extract Method(提煉函數(shù))?來(lái)解釋一段代碼的意義。畢竟臨時(shí)變量只在他所處的那個(gè)函數(shù)才有意義,局限性較大。函數(shù)則可以在對(duì)象的這個(gè)生命中都有用,并且可被其他對(duì)象使用。但有時(shí)候,當(dāng)局部變量使Extract Method(提煉函數(shù))難以進(jìn)行時(shí),就可以使用Introduce Explaining Variable (引入解釋性變量).

    6.?Split Temporary Variable 分解臨時(shí)變量

    分解臨時(shí)變量:(臨時(shí)變量不應(yīng)該被賦值超過(guò)一次)你的程序有某個(gè)臨時(shí)變量被賦值超過(guò)一次,它既不是循環(huán)變量,也不被用于收集計(jì)算結(jié)果。針對(duì)每次賦值,創(chuàng)造一個(gè)獨(dú)立、對(duì)應(yīng)的臨時(shí)變量

  • 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);??
  • ? ? ? ? 臨時(shí)變量有各種不同用途,其中某些用途會(huì)很自然的導(dǎo)致臨時(shí)變量被多次賦值。“循環(huán)變量”和“結(jié)果收集變量”就是典型的例子:循環(huán)變量會(huì)隨循環(huán)的每次運(yùn)行而改變; ??
    結(jié)果收集變量負(fù)責(zé)將“通過(guò)這個(gè)函數(shù)的運(yùn)算”而構(gòu)成的某個(gè)值收集起來(lái)。
    ? ? ? ? 除了這2種情況,還有很多臨時(shí)變量保存一段冗長(zhǎng)代碼的運(yùn)算結(jié)果,以便稍后使用。這種臨時(shí)變量應(yīng)該只被賦值一次。如果它們被賦值超過(guò)一次,
    就意味著它們?cè)诤瘮?shù)中承擔(dān)了一個(gè)以上的職責(zé)。如果臨時(shí)變量承擔(dān)多個(gè)責(zé)任,它就應(yīng)該被替換為多個(gè)臨時(shí)變量,每個(gè)變量只承擔(dān)一個(gè)責(zé)任。同一個(gè)臨時(shí)變量承擔(dān)2件不同的事情,會(huì)令代碼閱讀者糊涂。


    7.Remove Assigments to Parameters 移除對(duì)參數(shù)的賦值

    移除對(duì)參數(shù)的賦值:(不要對(duì)參數(shù)賦值)代碼對(duì)一個(gè) 參數(shù)賦值。以一個(gè)臨時(shí)變量取代該參數(shù)的位置。?
  • 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;???
  • }??
  • 如果參數(shù)是Object,容易誤賦值。采用final來(lái)防止誤用參數(shù):

    要清楚“對(duì)參數(shù)賦值”這個(gè)說(shuō)法的意思。如果你把一個(gè)名為foo的對(duì)象作為參數(shù)傳給某個(gè)函數(shù),那么“對(duì)參數(shù)賦值”意味著改變foo,使它引用另外一個(gè)對(duì)象。如果你在“被傳入對(duì)象”身上進(jìn)行什么操作,那沒(méi)什么問(wèn)題。這里只針對(duì)“foo被改而指向另一個(gè)對(duì)象”這種情況來(lái)討論。

  • <span?style="font-size:12px;">void?aMethod(Object??foo)?{??
  • ??????foo.modifyInSomeWay();??
  • ??????foo?=?anotherObject;??
  • }</span>??
  • 這樣的做法降低了代碼的清晰度,而且混用了按值傳遞和按引用傳遞這2種參數(shù)傳遞方式。
    在按值傳遞的情況下,對(duì)參數(shù)的任何修改,都不會(huì)對(duì)調(diào)用端造成任何影響。那些用過(guò)按引用傳遞方式的人可能會(huì)在這一點(diǎn)上犯糊涂。
    另一個(gè)讓人糊涂的地方時(shí)函數(shù)本體內(nèi)。如果你只以參數(shù)表示“被傳遞進(jìn)來(lái)的東西”。那么代碼會(huì)清晰地多,因?yàn)檫@種用法在所有語(yǔ)言都表現(xiàn)出相同語(yǔ)義。

    8. Replace Method with Method object 函數(shù)對(duì)象取代函數(shù)

    函數(shù)對(duì)象代替函數(shù):(大函數(shù)變成類(lèi))你有一個(gè)大型函數(shù),其中對(duì)局部變量的使用使你無(wú)法采用 Extract Method (提煉函數(shù))。將這個(gè)大型函數(shù)放進(jìn)一個(gè)單獨(dú)對(duì)象中,如此一來(lái)局部變量就成了對(duì)象內(nèi)的字段。然后你可以在同一個(gè)對(duì)象中將這個(gè)大型函數(shù)分解為多個(gè)小型函數(shù)。
  • class?Order...??
  • double?price()?{???
  • ????double?primaryBasePrice;??
  • ????double?secondaryBasePrice;???
  • ????double?tertiaryBasePrice;???
  • ????//?long?computation;?...???
  • }???
  • ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

    ? ? 或者可以采用static method

    ? ? ? ? 局部變量的存在會(huì)增加函數(shù)分解的難度。如果一個(gè)函數(shù)之中局部變量泛濫,那么想分解這個(gè)函數(shù)是非常困難的。Replace Temp with Query (以查詢?nèi)〈R時(shí)變量)可以幫助你減輕這一負(fù)擔(dān),但有時(shí)候你會(huì)發(fā)現(xiàn)根本無(wú)法拆解一個(gè)需要拆解的函數(shù)。這種情況下,應(yīng)該使用函數(shù)對(duì)象。

    9.Substitute Algorithm 替換算法

    替換算法:函數(shù)本體替換為另一個(gè)算法)你想要把某個(gè)算法替換為另一個(gè)更清晰地算法。將函數(shù)本體替換為另一個(gè)算法。??
  • 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?"";???
  • }???
  • ? ? ? 解決問(wèn)題有好幾種方法。算法也是如此。如果你發(fā)現(xiàn)做一件事可以有更清晰地方式,就應(yīng)該以較清晰地方式取代復(fù)雜的方式。“重構(gòu)”可以把一些復(fù)雜東西分解為較簡(jiǎn)單的小塊,但有時(shí)你就必須刪除整個(gè)算法,代之以簡(jiǎn)單的算法。隨著對(duì)問(wèn)題有了更多理解,你往往會(huì)發(fā)現(xiàn),在原先的做法之外,有更簡(jiǎn)單的解決方案,此時(shí)就需要改變?cè)瓉?lái)的算法。如果你開(kāi)始使用程序庫(kù),而其中提供的某些功能/特性與你自己的代碼重復(fù),那么你也需要改變?cè)鹊乃惴ā?br /> ? ? ? 有時(shí)候你會(huì)想要修改原先的算法,讓它去做一件與原先略有差異的事。這時(shí)候你也可以先把原先的算法替換為一個(gè)較易修改的算法,這樣后續(xù)的修改會(huì)輕松許多。使用這項(xiàng)重構(gòu)之前,請(qǐng)先確定自己盡可能分解了原先函數(shù)。替換一個(gè)巨大而復(fù)雜的算法是很困難的。只有先將它分解為較簡(jiǎn)單的小型函數(shù),然后你才能很有把握的進(jìn)行算法替換工作。

    總結(jié)

    以上是生活随笔為你收集整理的重构-改善既有代码的设计:重新组织函数的九种方法(四)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。