重構(gòu)不止是亂糟代碼整理,它提供了一種高效且受控的代碼代碼整理技術(shù)。
(一)重構(gòu)原則
1、手把手教何謂重構(gòu)
對(duì)軟件內(nèi)部結(jié)構(gòu)的重構(gòu)糟一種調(diào)整,目的亂糟是在不改變軟件可觀察行為的前提下,提高其可理解性,代碼降低其修改成本。手把手教
另一種解釋是重構(gòu)糟:使用一系列重構(gòu)手法,在不改變軟件可觀察行為的亂糟前提下,調(diào)整其結(jié)構(gòu)。代碼
2、手把手教為何重構(gòu)
改進(jìn)軟件設(shè)計(jì):如果沒(méi)有重構(gòu),重構(gòu)糟程序的亂糟設(shè)計(jì)會(huì)逐漸變質(zhì),重構(gòu)很像是在整理代碼,你所做的就是讓所有的東西回到應(yīng)處的位置上。
幫助找到bug:對(duì)代碼進(jìn)行重構(gòu),可以深入理解代碼的作為,在搞清楚程序結(jié)構(gòu)的同時(shí),想不把bug揪出來(lái)都難。
提高編程速度:良好的設(shè)計(jì)是快速開(kāi)發(fā)的根本,改善設(shè)計(jì)、提高可讀性,減少錯(cuò)誤,這些都是提高質(zhì)量。
3、何時(shí)重構(gòu)
任何情況下我都反對(duì)專(zhuān)門(mén)撥出時(shí)間進(jìn)行重構(gòu)。重構(gòu)本來(lái)就不是一件應(yīng)該特別撥出時(shí)間做的事情,重構(gòu)應(yīng)該隨時(shí)隨地的進(jìn)行。
三次法則
第一次做某件事情是只管去做;第二次做類(lèi)似的事情會(huì)產(chǎn)生反感;第三次再做類(lèi)似的事,你就應(yīng)該重構(gòu)
最常見(jiàn)的重構(gòu)時(shí)機(jī)是想給軟件添加新特性的時(shí)候;
重構(gòu)的另個(gè)一原動(dòng)力是:代碼的設(shè)計(jì)無(wú)法幫助我輕松的添加所需要的特性
修改錯(cuò)誤的時(shí)候,review代碼的時(shí)重構(gòu)
間接層和重構(gòu)
計(jì)算機(jī)科學(xué)是這樣一門(mén)科學(xué):它相信所有的問(wèn)題都可以通過(guò)增加一個(gè)間接層來(lái)解決。
大多數(shù)重構(gòu)都為程序引入了更多的間接層,重構(gòu)往往把大型的對(duì)象拆成多個(gè)小型的對(duì)象,把大型的函數(shù)拆成多個(gè)小型的函數(shù)。
但是,間接層是一把雙刃劍。每次把一個(gè)東西分成兩份,你就需要多管理一個(gè)東西。如果某個(gè)對(duì)象委托另一個(gè)對(duì)象,后者又委托另一個(gè)對(duì)象,程序會(huì)愈加難以閱讀。
何時(shí)不該重構(gòu):有時(shí)候既有代碼實(shí)在太混亂,重構(gòu)它還不如重新寫(xiě)一個(gè)來(lái)得簡(jiǎn)單。
重寫(xiě)而非重構(gòu)的一個(gè)清楚訊號(hào)是:現(xiàn)有代碼根本不能正常運(yùn)作。
(二)代碼的壞味道
1、重復(fù)代碼
如果你在一個(gè)以上的地點(diǎn)看到相同的程序結(jié)構(gòu),那么可以肯定:設(shè)法將它們合二為一,程序會(huì)變得更好 。
同一個(gè)類(lèi)中有相同的表達(dá)式:提煉出重復(fù)的代碼,然后讓兩個(gè)地方都調(diào)用被提煉出來(lái)的那一段代碼;
兩個(gè)互為兄弟的子類(lèi)內(nèi)含有相同的表達(dá)式:提煉出相同代碼,將它推入超類(lèi)內(nèi);
兩個(gè)毫不相干的類(lèi)中出現(xiàn):將重復(fù)的代碼提煉到一個(gè)獨(dú)立的類(lèi)中。
2、過(guò)長(zhǎng)的類(lèi)
擁有短函數(shù)的對(duì)象活得比較好、比較長(zhǎng)。間接層所能帶來(lái)的全部利益——解釋能力、共享能力、選擇能力——都是由小型函數(shù)支持的。
每當(dāng)感覺(jué)需要以注釋來(lái)說(shuō)明點(diǎn)什么的時(shí)候,我們就把需要說(shuō)明的東西寫(xiě)進(jìn)一個(gè)獨(dú)立的函數(shù)中。
如何確定提煉哪一段代碼?尋找注釋是一個(gè)很好的技巧。它們通常能指出代碼用途和實(shí)現(xiàn)手法之間的語(yǔ)義距離。如果代碼前方有一行注釋?zhuān)褪翘嵝涯悖嚎梢詫⑦@段代碼替換成一個(gè)函數(shù)。
條件表達(dá)式和循環(huán)常常也是提煉的信號(hào)。
3、過(guò)大的類(lèi)
如果想利用單個(gè)類(lèi)做太多的事情,其內(nèi)往往就會(huì)出現(xiàn)太多實(shí)力變量。
類(lèi)內(nèi)如果有太多代碼,也是代碼重復(fù)、混亂病最終走向死亡的源頭。
4、過(guò)長(zhǎng)參數(shù)列
太長(zhǎng)的參數(shù)列難以理解,太多的參數(shù)會(huì)造成前后不一致、不容易使用,而且一旦你需要更多數(shù)據(jù),就不得不修改它。如果將對(duì)象傳遞給函數(shù),大多數(shù)修改都將沒(méi)有必要。
5、發(fā)散式變化
如果某個(gè)類(lèi)經(jīng)常因?yàn)椴煌脑蛟诓煌姆较蛏习l(fā)生變化,那么此時(shí)也許將這個(gè)對(duì)象分成兩個(gè)會(huì)更好,這么一來(lái)每個(gè)對(duì)象就可以只因?yàn)橐环N變化而需要修改。
6、散彈式修改
如果沒(méi)遇到某種變化,你都必須在許多不同的類(lèi)內(nèi)做出許多小修改,你所面臨的壞味道就是散彈式修改。如果需要修改的代碼散布四處,你不但很難找到它們,也很容易忘記某個(gè)重要的修改。
把所有需要修改的代碼放進(jìn)同一個(gè)類(lèi)中,如果眼下沒(méi)有合適的類(lèi)可以安置這些代碼就創(chuàng)造一個(gè)。
7、依戀情結(jié)
對(duì)象技術(shù)的要點(diǎn)在于:將數(shù)據(jù)和對(duì)數(shù)據(jù)的操作行為包裝在一起.有一種經(jīng)典的氣味是:函數(shù)對(duì)某個(gè)類(lèi)的興趣高過(guò)對(duì)自己所處類(lèi)的興趣。某個(gè)函數(shù)為了計(jì)算某個(gè)值,從另一個(gè)對(duì)象那調(diào)用幾乎半打的取值函數(shù)。
一個(gè)函數(shù)往往會(huì)用到幾個(gè)類(lèi)的功能,那么它該置于何處?我們的原則是:判斷哪個(gè)類(lèi)擁有最大被此函數(shù)使用的數(shù)據(jù),然后就把這個(gè)函數(shù)和那些數(shù)據(jù)放在一起。
8、數(shù)據(jù)泥團(tuán)
很多地方看到相同的三四項(xiàng)數(shù)據(jù)一起出現(xiàn)。這些總是綁在一起出現(xiàn)的數(shù)據(jù)應(yīng)該擁有屬于他們自己的對(duì)象。
首先找到這些數(shù)據(jù)以字段形式出現(xiàn)的地方,將它們提煉到一個(gè)獨(dú)立的對(duì)象中。這么做的直接好處是可以將很多參數(shù)列縮短簡(jiǎn)化函數(shù)調(diào)用。
9、基本類(lèi)型偏執(zhí)
對(duì)象的一個(gè)極大價(jià)值在于:它們模糊了橫旦與基本數(shù)據(jù)和體積較大的類(lèi)之間的界限
對(duì)象技術(shù)的新手通常不愿意在小任務(wù)上運(yùn)用小對(duì)象——結(jié)合數(shù)值和比重的money類(lèi)、有一個(gè)起始值和一個(gè)結(jié)束值組成的range類(lèi)。將原本單獨(dú)存在的數(shù)值替換成對(duì)象,從而走出傳統(tǒng)的洞窟,進(jìn)入炙手可熱的對(duì)象世界。
10、switch驚悚現(xiàn)身
面向?qū)ο蟮囊粋€(gè)最明顯的特征是:少用switch語(yǔ)句一看到switch語(yǔ)句,就應(yīng)該考慮以多態(tài)來(lái)替換它。
如果只是在單一函數(shù)中有些選擇實(shí)例,且并不想改動(dòng)它們,那么多態(tài)就有點(diǎn)殺雞用牛刀了。
11、平行集成體系
每當(dāng)你為某個(gè)類(lèi)增加一個(gè)子類(lèi),必須也為另一個(gè)類(lèi)相應(yīng)增加一個(gè)子類(lèi)。
消除這種重復(fù)性的一般策略是:讓一個(gè)繼承體系的實(shí)例引用另一個(gè)繼承體系的實(shí)例。
12、冗余類(lèi)
某個(gè)類(lèi)原本對(duì)得起自己的身價(jià),但重構(gòu)使它身形縮水,不再做那么多工作,這個(gè)時(shí)候請(qǐng)讓這個(gè)類(lèi)莊嚴(yán)赴義吧。
13、夸夸其談未來(lái)性
企圖以各種各樣的鉤子和特殊情況來(lái)處理一些非必要的事情,這種懷味道就出現(xiàn)了。如果用到了那就值得去做,如果用不到那就不值得,只會(huì)擋你的路,所以把它挪開(kāi)吧。
如果你的某個(gè)抽象類(lèi)其實(shí)沒(méi)有起到太大的作用,函數(shù)上的某些參數(shù)未被使用…可以移除它們了。
14、令人迷惑的暫時(shí)字段
某個(gè)實(shí)例變量?jī)H為某種特定的情況而設(shè)。這樣的代碼讓人不易理解。在變量未被使用的情況下猜測(cè)當(dāng)初其設(shè)置目的,會(huì)讓你發(fā)瘋的。
15、過(guò)度耦合消息鏈
如果你看到用戶(hù)向一個(gè)對(duì)象請(qǐng)求另一個(gè)對(duì)象,然后再向后者請(qǐng)求另一個(gè)對(duì)象,然后再請(qǐng)求另個(gè)一對(duì)象……..這就是消息鏈。采用這種方式,意味著客戶(hù)代碼將與查找過(guò)程中的導(dǎo)航結(jié)構(gòu)緊密耦合。一旦對(duì)象間的關(guān)系發(fā)生任何變化,客戶(hù)端就不得不做出相應(yīng)的修改。
16、中間人
封裝往往伴隨著委托。你也許會(huì)看到某個(gè)類(lèi)接口有一半的函數(shù)都委托給其他類(lèi),這樣就是過(guò)度運(yùn)用。
17、狎昵關(guān)系
有時(shí)會(huì)看到兩個(gè)類(lèi)過(guò)于親密,話(huà)費(fèi)太多的時(shí)間去探究彼此的private成分。過(guò)分狎昵的類(lèi)必須拆散,幫它們劃清界線(xiàn),從而減少狎昵行徑。
繼承往往造成過(guò)度親密,因?yàn)樽宇?lèi)對(duì)超類(lèi)的了解總是超過(guò)后者的主觀愿望。如果你覺(jué)得該讓孩子獨(dú)立生活了,讓他離開(kāi)繼承。
18、異曲同工的類(lèi)
兩個(gè)函數(shù)做同一件事,卻有著不同的簽名。
19、不完美的類(lèi)庫(kù)
類(lèi)庫(kù)函數(shù)構(gòu)造的不夠好,又不能修改它們:
如果只想修改類(lèi)的一兩個(gè)函數(shù),可以引入外加函數(shù)。如果想要添加一大堆額外行為,建立一個(gè)新類(lèi)包含這些額外行為,讓其成為子類(lèi)。
20、純稚的數(shù)據(jù)類(lèi)
純稚的數(shù)據(jù)類(lèi)是指:它們擁有一些字段,以及用于訪(fǎng)問(wèn)(讀寫(xiě))這些字段的函數(shù),除此之外一無(wú)長(zhǎng)物。
封裝public字段;
恰當(dāng)封裝容器類(lèi)字段;
移除不應(yīng)修改的字段的設(shè)置函數(shù);
提煉調(diào)用函數(shù)以隱藏取值/設(shè)值函數(shù);
21、被拒絕的遺贈(zèng)
子類(lèi)只運(yùn)用了父類(lèi)的一部分函數(shù)和數(shù)據(jù)。為子類(lèi)建立一個(gè)兄弟類(lèi),將所有用不到的字段/函數(shù)下移至兄弟類(lèi),保證超類(lèi)的純粹;
22、過(guò)多的注釋
注釋之所以存在是因?yàn)榇a很糟糕 。注釋的最高境界——代碼即注釋。
當(dāng)你感覺(jué)需要撰寫(xiě)注釋時(shí),請(qǐng)先嘗試重構(gòu),試著讓所有的注釋都變得多余。
(三)重新組織函數(shù)
1、提煉函數(shù)
動(dòng)機(jī):看到一個(gè)過(guò)長(zhǎng)的函數(shù)或者一段需要注釋才能讓人理解用途的代碼,將這段代碼放一個(gè)獨(dú)立的函數(shù)中;
做法:創(chuàng)造一個(gè)新函數(shù),根據(jù)這個(gè)函數(shù)的意圖來(lái)命名它;
只要新函數(shù)的名稱(chēng)能夠以更好的方式昭示代碼意圖,你也應(yīng)該提煉它。但如果想不到一個(gè)更有意義的名稱(chēng)就別動(dòng)
將提煉的代碼從原函數(shù)復(fù)制到新建的目標(biāo)函數(shù)中;
將被提煉代碼段中需要讀取的局部變量,當(dāng)作參數(shù)傳遞給目標(biāo)函數(shù);
在源函數(shù)中,將被提煉代碼段替換為目標(biāo)函數(shù)調(diào)用。
2、內(nèi)聯(lián)函數(shù)
一個(gè)函數(shù)的本體與名稱(chēng)同樣清楚易懂。在函數(shù)調(diào)用點(diǎn)插入函數(shù)本體,然后移除該函數(shù)。
動(dòng)機(jī):一群組織不甚合理的函數(shù)。你可以將它們都內(nèi)聯(lián)到一個(gè)大函數(shù)中,再?gòu)闹刑釤挸鼋M織合理的小型函數(shù)。
使用的太多的間接層,使得系統(tǒng)中的所有函數(shù)都似乎只是對(duì)另一個(gè)函數(shù)的簡(jiǎn)單委托,造成在委托動(dòng)作之間暈頭轉(zhuǎn)向。
做法:
1、檢查函數(shù),確定不具備多態(tài);
如果子類(lèi)繼承了這個(gè)函數(shù),就不要將此函數(shù)內(nèi)聯(lián),因?yàn)樽宇?lèi)無(wú)法復(fù)寫(xiě)一個(gè)根本不存在的函數(shù)。
2、找出這個(gè)函數(shù)的所有調(diào)用點(diǎn);
3、將這個(gè)函數(shù)的所有調(diào)用點(diǎn)都替換成函數(shù)本體。
3、內(nèi)聯(lián)臨時(shí)變量
有一個(gè)臨時(shí)變量,只被一個(gè)簡(jiǎn)單的表達(dá)式賦值一次,而它妨礙了其他重構(gòu)手法。
將所有對(duì)該變量的引用動(dòng)作,替換為對(duì)它賦值的那個(gè)表達(dá)式自身
double basePrice = anOrder.basePrice();
return (base >10000 );
替換為:
return (anOrder.basePrice >1000);
4、以查詢(xún)?nèi)〈R時(shí)變量
你的程序以一個(gè)臨時(shí)變量保存某一表達(dá)式的運(yùn)算結(jié)果。將這個(gè)表達(dá)式提煉到一個(gè)獨(dú)立的函數(shù)中。將這個(gè)臨時(shí)變量的所有引用點(diǎn)替換為對(duì)新函數(shù)的調(diào)用。此后,新函數(shù)就可被其他函數(shù)使用。
double?basePrice = quantity * timePrice;
if(basePrice >?1000){
? ?return?basePrice *?09.5;
}?else?{
? ?return?basePrice *?0.98;
}
替換為:
if(basePrice() >?1000){
? ?return?basePrice *?09.5;
}?else?{
? ?return?basePrice *?0.98;
}
double?basePrice(){
? ?return?quantity * timePrice;
}
臨時(shí)變量只在所屬的函數(shù)中可見(jiàn),如果把臨時(shí)變量替換為一個(gè)查詢(xún),那么同一個(gè)類(lèi)中的所有函數(shù)都將可以獲得這個(gè)份信息,這將帶給你極大的幫助,使你能夠?yàn)檫@個(gè)類(lèi)編寫(xiě)更清晰的代碼。
5、引入注釋性變量
你有一個(gè)復(fù)雜的表達(dá)式。將該復(fù)雜表達(dá)式(或其中一部分)的結(jié)果放進(jìn)一個(gè)臨時(shí)變量,以此變量名稱(chēng)來(lái)解釋表達(dá)式用途。
if?((platform.toUpperCase().indexOf("MAC") >-1) && (browser.toUpperCase().indexOf("IE") >-1) && wasInitialized() &&?resize?>0){
? ?//do?smothing
}
替換為:
final?boolean?isMacOs = platform.toUpperCase().indexOf("MAC") >-1;
final?boolean?isIEBrowser = browser.toUpperCase().indexOf("IE") >-1;
final?boolean?wasResized = resize >0;
if(isMacOs && isIEBrowser && wasInitialized() && wasResized){
? ?//do smothing
}
表達(dá)式有可能非常復(fù)雜難以理解。這種情況下,臨時(shí)變量可以幫助你將表達(dá)式分解為比較容易管理的形式。
在條件邏輯中,你可以用這項(xiàng)重構(gòu)將每個(gè)條件子句提煉出來(lái),以一個(gè)良好命名的臨時(shí)變量來(lái)解釋對(duì)應(yīng)條件子句的意義。另一種情況是:在較長(zhǎng)的算法中,可以運(yùn)用臨時(shí)變量來(lái)解釋每一步運(yùn)算的意義。
6、分解臨時(shí)變量
你的程序有某個(gè)臨時(shí)變量被賦值超過(guò)一次,它既不是循環(huán)變量,也不被用于收集計(jì)算結(jié)果。針對(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);
替換為:
double?perimeter =?2?* (height + width);
System.out.println(perimeter);
double?area = height * width;
System.out.println(area);
如果臨時(shí)變量被賦值超過(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)兩件不同的事情會(huì)令代碼閱讀者糊涂
7、移除對(duì)參數(shù)的賦值
代碼對(duì)一個(gè)參數(shù)進(jìn)行復(fù)制。以一個(gè)臨時(shí)變量取代該參數(shù)的位置。
int?discount?(int?inputVal,?int?quantity,?int?yearToData){
? ?if(inputVal >?50) inputVal -=?2;
}
替換為:
int?discount?(int?inputVal,?int?quantity,?int?yearToData){
? ?int?result = inputVal;
? ?if(inputVal >?50) result -=?2;
}
如果代碼的語(yǔ)義是按引用傳遞的,請(qǐng)?jiān)谡{(diào)用段檢查調(diào)用后是否還使用了這個(gè)參數(shù)。
8、替換算法
想要把某個(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?prople[i];
? ? ? ?}
? ?}
? ?return?"";
}
(四)在對(duì)象之間搬移特性
在對(duì)象設(shè)計(jì)過(guò)程中,決定把責(zé)任放在哪兒是即使不是最重要的事,也是最重要的事之一。
常常只使用搬移函數(shù)和搬移字段簡(jiǎn)單地移動(dòng)對(duì)象行為,就可以解決這些問(wèn)題。如果這兩個(gè)重構(gòu)手法都需要用到,我會(huì)首先使用搬移字段,再使用搬移方法。
如果一個(gè)類(lèi)承擔(dān)了太多責(zé)任而變得臃腫不堪,這種情況下會(huì)使用提煉類(lèi)將一部分責(zé)任分離出去。如果一個(gè)類(lèi)變得太不負(fù)責(zé)任,使用將類(lèi)內(nèi)聯(lián)化將它融入到另一個(gè)類(lèi)中。
1、搬移函數(shù)
你的程序中,有個(gè)函數(shù)與其所駐類(lèi)之外的另個(gè)一類(lèi)進(jìn)行跟過(guò)的交流:調(diào)用后者或被后者調(diào)用。在該函數(shù)最長(zhǎng)引用的類(lèi)中建立一個(gè)有著類(lèi)似行為的新函數(shù)。將舊函數(shù)變成一個(gè)單純的委托函數(shù),或者將舊函數(shù)完全移除。
如果一個(gè)類(lèi)有太多行為,或如果一個(gè)類(lèi)與另一個(gè)類(lèi)有太多合作而高度耦合,就需要搬移函數(shù)??梢允窍到y(tǒng)中的類(lèi)更簡(jiǎn)單
2、搬移字段
程序中,某個(gè)字段被其所駐類(lèi)之外的另一個(gè)類(lèi)更多的用到。在目標(biāo)類(lèi)新建一個(gè)字段,修改原字段的所有用戶(hù),令他們改用新字段
3、提煉類(lèi)
某個(gè)類(lèi)做了應(yīng)該由兩個(gè)類(lèi)做的事。建立一個(gè)新類(lèi),將相關(guān)字段和函數(shù)從就類(lèi)搬到新類(lèi)。
4、將類(lèi)內(nèi)聯(lián)化
某個(gè)類(lèi)沒(méi)有做太多的事情,不在承擔(dān)足夠責(zé)任,不再有的那單獨(dú)存在的理由。將這個(gè)類(lèi)的所有特性搬移到另一個(gè)類(lèi)中,然后移除原類(lèi)。
5、隱藏“委托關(guān)系”
客戶(hù)通過(guò)一個(gè)委托類(lèi)來(lái)調(diào)用另一個(gè)對(duì)象。在服務(wù)類(lèi)上建立客戶(hù)所需要的所有函數(shù),用來(lái)隱藏委托關(guān)系。
封裝意味每個(gè)對(duì)象都應(yīng)該少了解系統(tǒng)的其他部分。一旦發(fā)生變化,需要了解這一變化的對(duì)象就會(huì)比較少。
如果某個(gè)客戶(hù)先通過(guò)服務(wù)對(duì)象的字段得到另一個(gè)對(duì)象,然后調(diào)用后者的函數(shù)。那么客戶(hù)就必須知曉這一層委托關(guān)系。萬(wàn)一委托關(guān)系變化,客戶(hù)也要相應(yīng)變化。
6、移除中間人
某個(gè)類(lèi)做了過(guò)多的簡(jiǎn)單委托。讓客戶(hù)直接調(diào)用委托類(lèi)。
每當(dāng)客戶(hù)要使用手委托類(lèi)的新特性時(shí),你就必須在服務(wù)端添加一個(gè)簡(jiǎn)單委托函數(shù)。隨著受委托類(lèi)的特性越來(lái)越多,這一過(guò)程會(huì)讓你很痛苦。
7、引入外加函數(shù)
你需要為提供服務(wù)的類(lèi)增加一個(gè)函數(shù),但你無(wú)法修改這個(gè)類(lèi)。在客戶(hù)類(lèi)中建立一個(gè)函數(shù),并以第一參數(shù)形式傳入一個(gè)服務(wù)類(lèi)實(shí)例。
Date newStart =?new Date(year, month, date +?1);
替換為:
Date?newStart = nextDay(nowDate);
private?static?Date?nextDay(Date?arg){
? ?retrun?new?Date(arg.getYear(), arg.getMonth(), arg.getDate() +?1);
}
如果可以修改源碼,你可以自行添加一個(gè)新函數(shù);如果不能,你就得在客戶(hù)端編碼,補(bǔ)足你要的那個(gè)函數(shù)
8、引入本地?cái)U(kuò)展
你需要為服務(wù)類(lèi)提供一些額外函數(shù),但你無(wú)法修改這個(gè)類(lèi)。建立一個(gè)新類(lèi),使它包含這些額外函數(shù)。讓這個(gè)擴(kuò)展品成為源類(lèi)的子類(lèi)或包裝類(lèi)。
(五)重新組織數(shù)據(jù)
1、自封裝字段
直接訪(fǎng)問(wèn)一個(gè)字段。為這個(gè)字段建立取值/設(shè)值函數(shù),并且只以這些函數(shù)來(lái)訪(fǎng)問(wèn)字段。
private?int?low, high;
boolean?includes(int?arg){
? ?retrun arg >= low && arg <= high;
}
替換為:
private?int?low, high;
boolean?includes(int?arg){
? ?retrun arg >= getLow() && arg <= getHigh();
}
int?getLow(){
? ?retrun low;
}
int?getHigh(){
? ?return?high;
}
在“字段訪(fǎng)問(wèn)方式”這個(gè)問(wèn)題上,存在兩種截然不同的觀點(diǎn):
在該變量定義所在的類(lèi)中,你可以自由的訪(fǎng)問(wèn)。
即使在這個(gè)類(lèi)中你也應(yīng)該只使用訪(fǎng)問(wèn)函數(shù)間接訪(fǎng)問(wèn)。
間接訪(fǎng)問(wèn)的好處是:子類(lèi)可以通過(guò)復(fù)寫(xiě)一個(gè)函數(shù)而改變獲取數(shù)據(jù)的途徑;它支持更靈活的數(shù)據(jù)管理方式,例如延遲初始化。
2、以對(duì)象取代數(shù)據(jù)值
你有一個(gè)數(shù)據(jù)項(xiàng),需要與其他數(shù)據(jù)和行為一起使用才有意義。將數(shù)據(jù)項(xiàng)變?yōu)閷?duì)象。
一開(kāi)始你肯能會(huì)用一個(gè)字符串來(lái)表示“電話(huà)號(hào)碼”概念,但是隨后你會(huì)發(fā)現(xiàn),電話(huà)號(hào)碼需要“格式化”、“區(qū)號(hào)”之類(lèi)的行為。這時(shí)候就需要為帶替換的數(shù)值新建一個(gè)類(lèi)。
3、將值對(duì)象改為引用對(duì)象
你從一個(gè)類(lèi)衍生出許多彼此相等的實(shí)例,希望將它們替換為同一個(gè)對(duì)象。將這個(gè)值對(duì)象變成引用對(duì)象。
4、將引用對(duì)象改為值對(duì)象
你有一個(gè)引用對(duì)象,很小且不可改變,而且不易管理。將它變成一個(gè)值對(duì)象。
5、以對(duì)象取代數(shù)組
你有一個(gè)數(shù)組,其中的元素各自代表不同的東西。以對(duì)象替換數(shù)組。對(duì)于數(shù)組中的每個(gè)元素,以一個(gè)字段來(lái)表示
6、復(fù)制“被監(jiān)視數(shù)據(jù)”
你有一些領(lǐng)域數(shù)據(jù)置身GUI控件中,而領(lǐng)域函數(shù)需要訪(fǎng)問(wèn)這些數(shù)據(jù)。將該數(shù)據(jù)復(fù)制到一個(gè)領(lǐng)域?qū)ο笾?。建立一個(gè)Observer模式,用以同步領(lǐng)域?qū)ο蠛虶UI對(duì)象內(nèi)的重復(fù)數(shù)據(jù)。
7、將單向關(guān)聯(lián)改為雙向關(guān)聯(lián)
兩個(gè)類(lèi)都需要使用對(duì)方特性,但其間只有一條單向連接。添加一個(gè)反向指針,并使修改函數(shù)能夠同時(shí)更新兩條連接。
8、將雙向關(guān)聯(lián)改為單向關(guān)聯(lián)
兩個(gè)類(lèi)之間有雙向關(guān)聯(lián),但其中一個(gè)類(lèi)如今不再需要另一個(gè)類(lèi)的特性。去除不必要的關(guān)聯(lián)。
9、以字面常量取代魔數(shù)
你有一個(gè)字面數(shù)值,帶有特別含義。創(chuàng)造一個(gè)常量,根據(jù)其意義為它命名,并將上述的字面數(shù)值替換為常量。
10、封裝字段
你的類(lèi)中存在一個(gè)public字段。將它聲明為private,并提供相應(yīng)的訪(fǎng)問(wèn)函數(shù)。
11、封裝集合
有個(gè)函數(shù)返回一個(gè)集合。讓這個(gè)函數(shù)返回該集合的一個(gè)只讀副本,并在這個(gè)類(lèi)中提供添加/移除集合元素的函數(shù)。
(六)簡(jiǎn)化條件表達(dá)式
1、分解條件表達(dá)式
有一復(fù)雜的條件語(yǔ)句。從if、then、else三個(gè)段落中分別提煉出獨(dú)立函數(shù)。
2、合并表達(dá)式
你有一系列條件測(cè)試,都得到相同結(jié)果。將這些測(cè)試合并為一個(gè)條件表達(dá)式,并將這個(gè)條件表達(dá)式提煉成一個(gè)獨(dú)立函數(shù)。
3、合并重復(fù)的條件代碼
在表達(dá)式的每個(gè)分支上都執(zhí)行了相同的一段代碼。將這段重復(fù)代碼搬移到條件表達(dá)式之外。
4、移除控制標(biāo)記
在一系列布爾表達(dá)式中,某個(gè)變量帶有”控制標(biāo)記”的作用。以break/return語(yǔ)句取代控制標(biāo)記。
5、以多態(tài)取代條件表達(dá)式
有個(gè)條件表達(dá)式根據(jù)對(duì)象類(lèi)型的不同而選擇不同的行為。將這個(gè)條件表達(dá)式的每個(gè)分支放進(jìn)一個(gè)子類(lèi)內(nèi)的覆寫(xiě)函數(shù)中,然后將原始函數(shù)聲明為抽象函數(shù)
(七)簡(jiǎn)化函數(shù)調(diào)用
1、函數(shù)改名
函數(shù)的名稱(chēng)未能揭示其用途。修改函數(shù)名稱(chēng)。
2、添加參數(shù)
某個(gè)函數(shù)需要從調(diào)用端得到更多信息。為此函數(shù)添加一個(gè)對(duì)象參數(shù),讓該對(duì)象帶僅函數(shù)所需信息。
3、移除參數(shù)
函數(shù)本體不再需要某個(gè)參數(shù)。去除參數(shù)。
4、分離查詢(xún)函數(shù)和修改函數(shù)
某個(gè)函數(shù)既返回對(duì)象狀態(tài)值,又修改對(duì)象值。建立兩個(gè)不同函數(shù),其中一個(gè)負(fù)責(zé)查詢(xún),另一個(gè)負(fù)責(zé)修改。
5、令函數(shù)攜帶參數(shù)
若干函數(shù)做了類(lèi)似的工作,但在函數(shù)本體中包含了不同的值。建立單一函數(shù),以參數(shù)表達(dá)那些不同的值。
有這樣兩個(gè)函數(shù):它們做著類(lèi)似的工作,但因少數(shù)幾個(gè)值致使行為略有不同。
在這種情況下,你可以將這些各自分離的函數(shù)同一起來(lái),并通過(guò)參數(shù)來(lái)處理那些變化情況,用以簡(jiǎn)化問(wèn)題。
6、以明確函數(shù)取代參數(shù)
你有一個(gè)函數(shù),其中完全取決于參數(shù)值而采用不同行為。針對(duì)該參數(shù)的每一個(gè)可能值,建立一個(gè)獨(dú)立函數(shù)。
如果某個(gè)參數(shù)有多種可能的值,而函數(shù)內(nèi)又以條件表達(dá)式檢查這些參數(shù)值,并根據(jù)不同參數(shù)值做出不同的行為,那么就應(yīng)該使用本項(xiàng)重構(gòu)。
7、保持對(duì)象完整
從某個(gè)對(duì)象中取出若干值,將它們作為某一次函數(shù)調(diào)用時(shí)的參數(shù)。改為傳遞整個(gè)對(duì)象。
8、以函數(shù)取代參數(shù)
對(duì)象調(diào)用某個(gè)函數(shù),并將所得結(jié)果作為參數(shù),傳遞給另一函數(shù),而接受該參數(shù)的函數(shù)本身也能夠調(diào)用前一個(gè)函數(shù)。讓參數(shù)接受者去除該參數(shù),直接調(diào)用前一個(gè)函數(shù)。
9、引入?yún)?shù)對(duì)象
某些參數(shù)總是很自然的同時(shí)出現(xiàn)。以一個(gè)對(duì)象取代這些參數(shù)。
10、移除設(shè)值函數(shù)
類(lèi)中某個(gè)字段在對(duì)象創(chuàng)建時(shí)被設(shè)值,然后不再改變。去掉該字段的所有設(shè)值函數(shù)。
11、隱藏函數(shù)
某個(gè)函數(shù),從來(lái)沒(méi)有被其他任何類(lèi)用到 。將函數(shù)修改為private。
12 、以工廠函數(shù)取代構(gòu)造函數(shù)
希望在創(chuàng)建對(duì)象時(shí)不僅僅是做簡(jiǎn)單的建構(gòu)動(dòng)作 。將構(gòu)造函數(shù)替換為工廠函數(shù)。
(八)處理概括關(guān)系
1、字段上移
兩個(gè)子類(lèi)擁有相同的字段。將該字段移至超類(lèi)。
2 、函數(shù)上移
有些函數(shù)在各子類(lèi)中產(chǎn)生完全相同的結(jié)果。將該函數(shù)移至超類(lèi)。
3 、構(gòu)造函數(shù)本體上移
各個(gè)子類(lèi)中有一些構(gòu)造函數(shù)本體幾乎完全一致 。在超類(lèi)中新建一個(gè)構(gòu)造函數(shù),并在子類(lèi)構(gòu)造函數(shù)中調(diào)用它。
4、函數(shù)下移
超類(lèi)中的某個(gè)函數(shù)只與部分(而非全部)子類(lèi)用到。將函數(shù)移到相關(guān)的子類(lèi)中。
5、字段下移
超類(lèi)中的某個(gè)字段只被部分(而非全部)子類(lèi)用到。將字段移到需要它的子類(lèi)中。
6、提煉子類(lèi)
類(lèi)中的某些特性只被某些(而非全部)實(shí)例用到。新建一個(gè)子類(lèi),將上述部分的特性移到子類(lèi)中。
7、提煉超類(lèi)
兩個(gè)類(lèi)有相似特性。為這兩個(gè)類(lèi)建立一個(gè)超類(lèi),將相同特性移至超類(lèi)。
8、提煉接口
若干客戶(hù)使用類(lèi)接口中的同一子集,或兩個(gè)類(lèi)的接口有部分相同。將相同的子集提煉到一個(gè)獨(dú)立接口中。
9、折疊繼承體系
超類(lèi)和子類(lèi)之間無(wú)太大區(qū)別。將它們合為一體。
10、塑造模板函數(shù)
子類(lèi)中某些函數(shù)以相同順序執(zhí)行類(lèi)似操作,但各操作細(xì)節(jié)略有不同。將操作放進(jìn)獨(dú)立函數(shù)(保持簽名相同),然后將它們移至超類(lèi)。
11、以委托取代繼承
某個(gè)子類(lèi)只使用超類(lèi)接口中的一部分或根本不需要繼承而來(lái)的數(shù)據(jù)。子類(lèi)新建字段保存超類(lèi),調(diào)整子類(lèi)函數(shù)為委托超類(lèi),取消繼承關(guān)系。
12、以繼承取代委托
你在兩個(gè)類(lèi)中使用委托關(guān)系,并經(jīng)常為整個(gè)接口編寫(xiě)許多極簡(jiǎn)單的委托函數(shù)。`讓委托類(lèi)繼承受托類(lèi)。
來(lái)源:www.jianshu.com/p/3f04b6aebad2
免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺(tái)立場(chǎng),如有問(wèn)題,請(qǐng)聯(lián)系我們,謝謝!