重構(gòu)和單元測(cè)試是程序員的兩大法寶,他們的作用就像空氣和水對(duì)于人一樣,平凡,不起眼,但是意義深重。預(yù)善事,必先利器,本文就介紹怎樣在Eclipse中進(jìn)行重構(gòu)。
?
本文介紹了Eclipse支持的重構(gòu)種類,它們的含義,以及怎樣重構(gòu)。本文同時(shí)也可以作為學(xué)習(xí)重構(gòu)知識(shí)的快速手冊(cè)。
?
什么是重構(gòu)
重構(gòu)是指在保持程序的全部功能的基礎(chǔ)上改變程序結(jié)構(gòu)的過(guò)程。重構(gòu)的類型有很多,如更改類名,改變方法名,或者提取代碼到方法中。每一次重構(gòu),都要執(zhí)行一系列的步驟,這些步驟要保證代碼和原代碼相一致。
?
為什么重構(gòu)很重要???
???????? 手工重構(gòu)時(shí),很容易在代碼中引入錯(cuò)誤,例如拼寫(xiě)錯(cuò)誤或者漏掉了重構(gòu)的某一步。為了防止引入錯(cuò)誤,在每次重構(gòu)前后,都要執(zhí)行充分的測(cè)試。你可能會(huì)好奇重構(gòu)是否是值得的。
重構(gòu)的理由很多。你可能想要更新一段代碼很爛的程序。或者最初的設(shè)計(jì)隊(duì)伍都不在了,現(xiàn)在隊(duì)伍中每人了解這些程序。為了更新,你必須要重新設(shè)計(jì)構(gòu)建程序來(lái)滿足你的需求。另一個(gè)原因是原來(lái)的設(shè)計(jì)無(wú)法使你將新的特性添加進(jìn)去。為了添加進(jìn)去,你要重構(gòu)這些代碼。第三個(gè)原因是一個(gè)自動(dòng)重構(gòu)的工具可以為你自動(dòng)生成代碼,例如Eclipse中的重構(gòu)功能。使用重構(gòu),你可以在重寫(xiě)盡量少的代碼和仍保持軟件功能的同時(shí),使代碼的邏輯性更好。
?
?
測(cè)試
在重構(gòu)時(shí),測(cè)試是十分重要的。應(yīng)為重構(gòu)改變了代碼的結(jié)構(gòu),你要保證重構(gòu)后代碼的功能沒(méi)有被改變。手工重構(gòu)時(shí),一個(gè)好的測(cè)試套是必須的。使用自動(dòng)重構(gòu)工具是,測(cè)試也是必要的,但不需要很頻繁,應(yīng)為自動(dòng)重構(gòu)工具不會(huì)產(chǎn)生手工重構(gòu)時(shí)的那些錯(cuò)誤,如拼寫(xiě)錯(cuò)誤。
在Eclipse中可以使用JUnit方便的為程序創(chuàng)建測(cè)試代碼,具體方法不在本文描述。
?
?
Eclipse中的重構(gòu)
JDT,Eclipse中的Java插件,能夠?qū)ava項(xiàng)目,類,或成員進(jìn)行多種類型的自動(dòng)重構(gòu)。可以采取多種方法快速的為Java項(xiàng)目中的某個(gè)元素進(jìn)行重構(gòu)。
為某些元素進(jìn)行重構(gòu)的前提是你必須選中他們。你可以在多個(gè)視圖中選擇這些元素,像大綱視圖或包瀏覽視圖。可以按住Ctrl或Shift鍵,在視圖中選擇多個(gè)元素。另外一種選擇的方法是使該元素的編輯區(qū)高亮顯示,或者把鼠標(biāo)定位到源程序文件。在選中希望重構(gòu)的元素后,可以從重構(gòu)菜單的下拉項(xiàng)選擇重構(gòu),也可以從右鍵單擊后彈出菜單中選擇重構(gòu)子菜單。同時(shí),Eclipse還提供了重構(gòu)的快捷鍵操作。
某些重構(gòu)可以應(yīng)用在任意元素上,有些則只能用在特定類型的元素上,如類或方法。在本文的最后的表格中,列出了重構(gòu)能夠應(yīng)用的元素類型,以及重構(gòu)的快捷鍵。
在Eclipse中,所有的重構(gòu)都能夠在正式執(zhí)行之前預(yù)覽一下。在重構(gòu)對(duì)話框中點(diǎn)擊“預(yù)覽”按鈕,可以查看所有將要被改變的地方。唯一沒(méi)有預(yù)覽按鈕的的重構(gòu)是Pull Up,在它的重構(gòu)向?qū)е校阶詈螅A(yù)覽面板總會(huì)出現(xiàn)。可以將其中的個(gè)別變化反選掉,這樣這些改變就不會(huì)生效。
?
?
撤銷和重做
?????? 在重構(gòu)菜單中有撤銷和重做項(xiàng)。他們和編輯菜單中的撤銷重做不同。即使重構(gòu)改變了很多文件,編輯菜單中的撤銷重做只會(huì)更改當(dāng)前文件。重構(gòu)菜單中的撤銷和重做則會(huì)對(duì)一次重構(gòu)的所有文件進(jìn)行撤銷和重做操作。但是在使用時(shí),它們有一定的限制。
重構(gòu)后,無(wú)論重構(gòu)改變了文件與否,如果任一個(gè)文件被另外改變而且保存了,你就無(wú)法撤銷或重做這個(gè)重構(gòu)。假如一個(gè)文件在重構(gòu)中被修改了,然后又被編輯了,但是還沒(méi)有保存,這時(shí)就會(huì)有錯(cuò)誤信息提示,如果你想要撤銷或重做該重構(gòu),必須撤銷未保存的文件。
只要注意到以上的限制條件,你就可以隨心所欲的對(duì)重構(gòu)進(jìn)行撤銷或重做。你甚至能夠編譯,運(yùn)行你的程序測(cè)試一下,然后再撤銷該重構(gòu),只要你沒(méi)有改變并保存任何文件。
?
Eclipse中的重構(gòu)類型
?????? 如果你看一下Eclipse的重構(gòu)菜單,可以看到四部分。第一部分是撤銷和重做。其他的三部分包含Eclipse提供的三種類型的重構(gòu)。
第一種類型的重構(gòu)改變代碼的物理結(jié)構(gòu),像Rename和Move。第二種是在類層次上改變代碼結(jié)構(gòu),例如Pull Up和Push Down。第三種是改變類內(nèi)部的代碼,像Extract Method和Encapsulate Field。這三部分的重構(gòu)列表如下。
?
類型1 物理結(jié)構(gòu)
l???????? Rename
l???????? Move
l???????? Change Method signature
l???????? Convert Anonymous Class to Nested
l???????? Convert Member Type to New File
?
類型2 類層次結(jié)構(gòu)
l???????? Push Down
l???????? Push Up
l???????? Extract Interface
l???????? Generalize Type (Eclipse 3)
l???????? User Supertype Where Possible
類型3 類內(nèi)部結(jié)構(gòu)
l???????? Inline
l???????? Extract Method
l???????? Extract Local Variable
l???????? Extract Constant
l???????? Introduce Parameter
l???????? Introduce Factory
l???????? Encapsulate Field
?
?
?
Rename:
?????? Rename用來(lái)改變一個(gè)Java元素的名字。雖然你可以手工改變Java文件Java元素的名字,但是這樣不能自動(dòng)更新所有引用它們的文件或Java元素。你必須在項(xiàng)目中搜索文件然后手工替換這些引用。很可能你就會(huì)漏掉一個(gè)或者改錯(cuò)一個(gè)。Rename 重構(gòu)會(huì)智能的更新所有有此引用的地方。
?????? 有時(shí)候,Java元素的名字不是很明了,或者它的功能已經(jīng)改變了。為了保持代碼的可讀性,該元素的名字也要更新。使用Rename重構(gòu),能夠十分快捷的更新元素的名字和所有引用它的地方。
?????? 要為一個(gè)Java元素改名,在包瀏覽視圖或大綱視圖選中該元素,從重構(gòu)菜單中選擇Rename項(xiàng),或者使用快捷鍵Alt+Shift+R。Rename對(duì)話框會(huì)出現(xiàn)。在這里添入新的名字,選擇是否更新該元素的引用。點(diǎn)擊預(yù)覽按鈕,會(huì)打開(kāi)預(yù)覽窗口,在這里,你可以看到那些內(nèi)容會(huì)被改變。點(diǎn)擊OK按鈕,重構(gòu)結(jié)束。
?
Move
?????? Move和Rename很相似。它用來(lái)把元素從一個(gè)位置移動(dòng)到另一個(gè)位置。它主要用來(lái)將類從一個(gè)包移動(dòng)到另一個(gè)包。選中要移動(dòng)的元素,從重構(gòu)菜單中選擇Move,或者使用快捷鍵,Alt+Shift+V,在彈出窗口中選擇要移動(dòng)的目的地。你仍然可以用預(yù)覽功能檢查一下有什么改變,也可以按OK按鈕直接讓其生效。
?
?
Change Method Signature
?????? 更改方法簽名能夠改變參數(shù)名,參數(shù)類型,參數(shù)順序,返回類型,以及方法的可見(jiàn)性。也可以添加,刪除參數(shù)。
?????? 要執(zhí)行此重構(gòu),選擇要重構(gòu)的方法,選中重構(gòu)菜單的更改方法簽名項(xiàng),會(huì)出現(xiàn)更改方法簽名對(duì)話框。
?
在此對(duì)話框中選擇方法的修飾詞,返回類型,參數(shù)。參數(shù)的添加,修改,移動(dòng),刪除可以通過(guò)右邊的按鈕控制。當(dāng)添加新的參數(shù)時(shí),會(huì)自動(dòng)賦予默認(rèn)值。凡是調(diào)用此方法的地方都會(huì)用此默認(rèn)值作為參數(shù)輸入。
?????? 改變方法簽名可能在方法中導(dǎo)致問(wèn)題,如果有問(wèn)題,當(dāng)你點(diǎn)擊預(yù)覽或OK時(shí),會(huì)被標(biāo)記出來(lái)。
??????
?
Move Members Type to New File
?????? 此重構(gòu)將嵌套類轉(zhuǎn)為一個(gè)單獨(dú)類。將會(huì)創(chuàng)建一個(gè)新的Java文件包含此嵌套類。選中要重構(gòu)的類,在重構(gòu)菜單上選擇Move Member Type to New File項(xiàng),在彈出的對(duì)話框中添入要?jiǎng)?chuàng)建的實(shí)例的名字。
?
??????
?
Push Down
?????? 此重構(gòu)將算中的方法和成員從父類中移動(dòng)到它的直接子類中,所有下推的方法都可選作為一個(gè)抽象方法留在父類中。下推重構(gòu)對(duì)于重新構(gòu)建項(xiàng)目設(shè)計(jì)十分有用。
????????
選擇若干方法或成員,從重構(gòu)菜單中選擇下推項(xiàng),彈出下推對(duì)話框。
????????
?????? 在此對(duì)話框中,可以分別選擇方法或成員,所有選中元素都會(huì)移動(dòng)到當(dāng)前類的子類中。當(dāng)點(diǎn)擊Add Required按鈕時(shí),所有已選擇元素所必需的元素也會(huì)自動(dòng)選上,此行為并不能保證所有必須的元素都能自動(dòng)選中,還是需要人工確認(rèn)。當(dāng)有方法被選中時(shí),編輯按鈕就會(huì)可用,點(diǎn)擊編輯按鈕,彈出編輯對(duì)話框。在其中可以選擇為選中方法在當(dāng)前類中遺留抽象方法,還是在當(dāng)前類中刪除這些方法。雙擊一天選中的方法,也可以打開(kāi)編輯對(duì)話框。在方法的Action列點(diǎn)擊,會(huì)出現(xiàn)一個(gè)下拉列表,可以在其中選擇遺留抽象方法還是在當(dāng)前類中刪除方法。按回車鍵確認(rèn)編輯結(jié)果。
?
?
Pull Up
?????? 上移與下推類似,也是在類之間移動(dòng)方法和成員。上移將方法或成員從一個(gè)類移動(dòng)到它的一個(gè)父類中。選中若干個(gè)方法或成員,在重構(gòu)菜單中選擇上移項(xiàng),上移向?qū)яR上會(huì)出現(xiàn)。
?????? 在選擇目標(biāo)類多選框中,列出了當(dāng)前類繼承的所有父類。你只能將方法或成員移動(dòng)到它們其中的一個(gè)里面。
?????? 如果在選中方法的Action列,被設(shè)置成在目標(biāo)類中聲明抽象方法,那么在目標(biāo)類的非抽象子類中創(chuàng)建必須的方法選項(xiàng)變?yōu)榭蛇x。當(dāng)它選中時(shí),目標(biāo)類的所有子類,如果它們中沒(méi)有選中的方法,則會(huì)為它們創(chuàng)建選中的方法。
?????? 和在下推中一樣,選擇多個(gè)方法,點(diǎn)擊編輯按鈕,或者雙擊一個(gè)方法,都會(huì)打開(kāi)編輯成員對(duì)話框。其中有兩個(gè)選項(xiàng),上移和在目標(biāo)類中聲明抽象方法。上移只是簡(jiǎn)單的復(fù)制方法到到父類中,并提供選擇是否在當(dāng)前類中刪除該方法。在目標(biāo)類中聲明抽象方法會(huì)在父類中創(chuàng)建一個(gè)選中方法的抽象方法,如果父類不是抽象類則置為抽象類,最后選中方法留在當(dāng)前類中。和在下推中一樣,也可以點(diǎn)擊Action列,可以在出現(xiàn)的下拉列表中選擇。
?????? 如果方法的Action列選為上移,在下一步的向?qū)е校瑢?huì)要求你選擇是否在當(dāng)前類中刪除這些方法,選中的方法會(huì)在當(dāng)前類中被刪除。
?????? 在向?qū)У娜我庖徊蕉伎梢园赐瓿砂粹o,結(jié)束重構(gòu)操作,此時(shí)按照默認(rèn)規(guī)則進(jìn)行重構(gòu)。
?
?
Extract Interface
?????? 提煉接口可以從一個(gè)存在的類中創(chuàng)建一個(gè)接口。你可以選擇在接口中包含著個(gè)類的那些方法。選中一個(gè)類,從重構(gòu)菜單選擇提煉接口項(xiàng),就可以打開(kāi)提煉接口對(duì)話框。
?????? 這此對(duì)話框中添入接口的名字,選擇希望包含的方法,在這個(gè)列表里面只列出了公共方法。選中改變對(duì)類[當(dāng)前類名]的應(yīng)用為對(duì)接口的引用選擇框,將把所有對(duì)當(dāng)前類的引用更新為對(duì)此接口的引用。
?
?
Generalize Type
?????? 泛化類型重構(gòu)可以將一個(gè)聲明對(duì)象的類型改變?yōu)樗某悾x擇變量,參數(shù),對(duì)象成員,方法返回類型,然后選擇重構(gòu)菜單的泛化類型項(xiàng)。在打開(kāi)的泛化類型對(duì)話框,選擇希望的新類型,然后點(diǎn)擊完成按鈕,結(jié)束重構(gòu)。
?
?
Use Supertype Where Possible
?????? 使用超類會(huì)將對(duì)一個(gè)特定類型的引用改變?yōu)閷?duì)它的超類的引用。選擇一個(gè)類,選中重構(gòu)菜單的使用超類項(xiàng),會(huì)打開(kāi)使用超類對(duì)話框。選中希望的超類類型,點(diǎn)擊完成按鈕完成重構(gòu)。重構(gòu)后,instanceof 表達(dá)式也會(huì)做相應(yīng)的替換。
?
?
?
Inline
?????? 內(nèi)聯(lián)是用代碼或值來(lái)取代調(diào)用方法的地方,靜態(tài)final對(duì)象成員,或局部變量。比如說(shuō),如果你內(nèi)聯(lián)一個(gè)方法調(diào)用,這個(gè)調(diào)用的地方就會(huì)被替換為該方法體。要內(nèi)聯(lián)一個(gè)方法,靜態(tài)final對(duì)象成員,局部變量,選中這些元素,在重構(gòu)菜單中選擇內(nèi)聯(lián)項(xiàng),或者使用快捷鍵Alt + Ctrl + I。在隨后打開(kāi)的內(nèi)聯(lián)對(duì)話框,你可以選擇是否要內(nèi)聯(lián)所有的調(diào)用,或者是選擇的調(diào)用。如果選擇所有調(diào)用,你還可以選擇是否刪除聲明本身。