重构-改善既有代码的设计:重新组织函数的九种方法(四)
??函數(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ù)的用途 。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
一個(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ù)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
? ? ?有時(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á)式自身? ? ?? ? ? ? ? ? ? ? ? ? ?
? ? ? ? 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)用。
? ?? 以查詢代替臨時(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á)式用途。? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
? ? 在條件邏輯中,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í)變量
結(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ù)的位置。?如果參數(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)討論。
這樣的做法降低了代碼的清晰度,而且混用了按值傳遞和按引用傳遞這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ù)。? ? 或者可以采用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è)算法。??總結(jié)
以上是生活随笔為你收集整理的重构-改善既有代码的设计:重新组织函数的九种方法(四)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 重构-改善既有代码的设计:对象之间移动特
- 下一篇: 重构-改善既有代码的设计:重构原则(二)