重構是對軟件內部結構的一種調整,目的是在不改變軟件行為的前提下,提高其可理解性,降低其修改成本。開發人員可以使用一系列重構準則,在不改變軟件行為的前提下,調整軟件的結構。有很多種原因,開發人員應該重構代碼,例如之前的開發人員代碼寫得很爛、自己以前設計時有缺陷、需求變更需要添加一些新的功能或修改原有功能等等。Martin Fowler在其著名的<<Refactoring—ImPRoving the Design of Existing Code>>一書中談到了為何重構的幾點原因:1. 重構可以改進軟件設計 如果不進行重構,程序的設計會變得越來越糟糕。通常程序員只為短期的目的,或者在沒有完全理解整體設計的時候,就開始修改代碼,這樣程序將會逐漸失去自己的結構,程序員也愈來愈難通過閱讀源碼理解原本設計,相信對此每一個開發人員都深有體會。代碼結構的流失是累積性的,愈難看出代碼所代表的意思,就越難保護其中的設計,于是設計也將變得越來越糟糕,經常性重構可以幫助維持設計該有的形態。2. 重構使軟件更易被理解 很多開發人員認為代碼只要能夠運行起來就可以了,筆者剛開始做開發的時候也是這么認為的,也寫過很多的垃圾代碼,也因此吃了不少苦頭。 也許有些人可能會認為自己可能不久就會離開所在的職位,不必在意代碼的質量,但作為一個開發人員來說,寫出漂亮的代碼是最基本的素質。 在軟件的不斷修改過程中,代碼的可讀性越來越差也是會慢慢累積的,但這不要緊,只要記得持續重構,就能使自己的代碼更容易被理解。 3. 重構可以協助找到Bugs 對代碼的理解,可以更容易找到bug,在重構的同時,也能夠更好的理解代碼及其行為,從而通過重構能夠幫助開發人員寫出更強壯的代碼。 4. 重構可以提高編程的速度 良好的設計是快速軟件開發的根本,如果沒有良好的設計,也許開始的一段時間開發人員的進展迅速,但是惡劣的設計很快就會使開發速度慢下來。也許把時間花在調試上的時間會越來越多,修改的時間會越來越長,而且這會是一個惡性的循環。良好的設計是維持軟件開發速度的根本,重構可以幫助開發人員更快速地開發軟件,因為它能夠阻止系統的設計變質,能夠提高代碼的可讀性。
使用Eclipse進行代碼重構 重構是軟件開發過程中保證代碼質量非常重要的手段,而手動進行重構代碼的話,很容易引入一些低級錯誤(例如,單詞拼寫錯誤),從而導致浪費大量不必要的時間。Eclipse為重構提供了很強大的支持,很大程度上用戶不必為重構的筆誤而再煩惱。要使用Eclipse的重構功能,可以先選擇相應的java元素(Java工程中的資源,包括工程、文件、方法、變量等),通過右鍵菜單選擇Refactor菜單下的重構功能.在Eclipse中,可以簡單的把重構分為結構性重構、類級別重構和類內部重構,每種類型的重構又分別包含了一些具體的實現,接下來將分別介紹Eclipse如何對Java元素進行重構。結構性重構 結構性重構涉及到JAVA元素的物理結構的改變,包括“Rename”、“Move”、“Change Method Signature”、“Convert Anonymous Class to Nested”和“Move Member Type to New File”,下面將一一介紹這些重構在Eclipse中的實現。1. Rename Rename重構的功能就是重命名Java元素。雖然可以通過手動修改文件的文件名或其它Java元素的名稱,但這種方式不會更新與此Java元素相關聯的引用,用戶必須手動查找和此Java元素相關的位置,然后進行手動修改。通過手動修改名稱的方式,造成筆誤的可能性會太太增加。通過Eclipse提供的Rename的功能,Eclipse會自動完成更新相關引用的操作。當Java元素的命名不清晰或功能發生改變的時,為了保持代碼的可讀性,可以通過Eclipse的重構功能重命名Java元素。選擇相應的Java元素,選擇右鍵Refactor菜單下的Rename菜單,可以對當前選擇的元素進行重命名,在彈出的重命名對話框中修改相應的元素名稱即可,例如修改一個包的重命名,如圖2所示。
圖2 Rename對話框
要修改包名的同時,可以選擇是否更新引用和更新子目錄,甚至是非Java文件也可以選擇性的更新。選擇Preview按鈕可以預覽重命名重構后的效果,如圖3所示。
圖3 預覽重命名包名
可以查看預覽的內容是否一致,確認是否要進行重命名的重構。可以進行重命名的Java元素有Java項目、Java文件、包、方法和屬性字段等。 提示:非Java項目和Java文件等也可以通過重構菜單的Rename進行重命名。2. Move Move的重構和Rename的重構類似,它可以把一個Java元素從一個地方移動到另一個地方,Move的重構主要用來移動一個類到不同的包下。首先選中一個Java文件,選擇Refactor菜單下的Move菜單項,彈出Move的重構對話框,如圖4所示。
圖4 Move對話框
可以選擇是否更新引用,設定移動文件重構的一些參數。提示:也可以通過拖動的方式把一個文件從一個包移動到另一個包,實現移動文件的重構。3. Change Method Signature “Change Method Signature”重構的功能是改變方法的定義,例如改變方法的參數名稱、類型和個數、返回值的類型,方法的可見性以及方法的名稱等。 要改變方法的定義,可以先選擇方法,通過右鍵菜單選擇Refactor菜單的“Change Method Signature”子菜單項,彈出“Change Method Signature”對話框,如圖5所示。
圖5 “Change Method Signature”對話框
可以通過“Change Method Signature”對話框改變方法的參數名稱、類型和個數、返回值的類型,方法的可見性以及方法名稱等。 4. Convert Anonymous Class to Nested “Convert Anonymous Class to Nested”重構的功能是把匿名類改成內部類,這樣同一個類的其它部分也可以共享此類了。 例如有例程1所示的類。 例程1 KeyListenerExample.java
public class KeyListenerExample { Display display; Shell shell; KeyListenerExample() { display = new Display(); shell = new Shell(display); shell.setSize(250, 200); shell.setText("A KeyListener Example"); Text text = new Text(shell, SWT.BORDER); text.setBounds(50, 50, 100, 20); text.addKeyListener(new KeyListener() { public void keyPressed(KeyEvent e) { System.out.println("key Pressed -" + e.character); } public void keyReleased(KeyEvent e) { System.out.println("key Released -" + e.character); } }); shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) display.sleep(); } display.dispose(); } public static void main(String[] args) { new KeyListenerExample(); } }在KeyListenerExample類有一個匿名類,實現了KeyListener接口,可以把這個匿名類改成內部類,首先選擇匿名類,右鍵選擇Refactor的“Convert Anonymous Class to Nested”菜單,輸入內部類的名稱,如圖6所示。
圖6 “Convert Anonymous Class to Nested”對話框
重構后的結果是Eclipse為此創建了一個內部類,名稱為TestKeyListener,重構后的代碼如例程2所示。 例程2 重構后的KeyListenerExample.java
public class KeyListenerExample { private final class TestKeyListener implements KeyListener { public void keyPressed(KeyEvent e) { System.out.println("key Pressed -" + e.character); } public void keyReleased(KeyEvent e) { System.out.println("key Released -" + e.character); } } Display display; Shell shell; KeyListenerExample() { display = new Display(); shell = new Shell(display); shell.setSize(250, 200); shell.setText("A KeyListener Example"); Text text = new Text(shell, SWT.BORDER); text.setBounds(50, 50, 100, 20); text.addKeyListener(new TestKeyListener()); while (!shell.isDisposed()) { if (!display.readAndDispatch()) display.sleep(); } display.dispose(); } public static void main(String[] args) { new KeyListenerExample(); } }也可以通過“Convert Anonymous Class to Nested”對話框定義新生成的內部類的可訪問性。 5. Move Member Type to Top Level 通過“Move Member Type to Top Level”的重構方式,可以把內部類改成非內部類,并且重新創建一個新的文件,這樣其它的類就可以共享此類。 例程2創建了一個內部類TestKeyListener,現在可以通過“Move Member Type to Top Level”重構的方式,把TestKeyListener放入一個單獨的類中。首先選擇TestKeyListener類,從右鍵菜單Refactor中選擇“Move Member Type to Top Level”,打開“Move Member Type to Top Level”對話框,如圖7所示。
圖7 “Move Member Type to Top Level”對話框
通過上面“Move Member Type to Top Level”重構,可以把內部類改成非內部類。提示:有些時候,重構并不是一步完成的,可以一步一步重構,例如,首先把匿名類改成內部類,再接著把內部類改成非內部類。
類級別重構 類級別重構有如下一些: 1. Push Down “Push Down”重構功能是把父類的方法和屬性移動到所有的子類中,父類的方法可以選擇性的保留抽象方法。首先選擇父類,右鍵選擇Refactor菜單的“Push Down”菜單項,可以通過“Push Down”對話框選擇重構,如圖8所示。
圖8 “Push Down”對話框
“Push Down”重構在重新設計類的時候是非常有用的,它可以比較有較的改善類的繼承關系,清楚定義類的行為。 2. Pull Up “Pull Up”重構和“Push Down”重構正好相反,它的作用是把方法和屬性移動到其父類中去。選擇需要重構的子類,從右鍵菜單選擇Refactor菜單的“Pull up”菜單項,通過“Pull Up”對話框進行重構,如圖9所示。
圖9 “Pull Up”對話框
提示:“Pull Up”重構和“Push Down”重構后可能會出錯,在使用此重構的同時,應該先弄清楚某些方法中是否有引用到其它方法或屬性。 3. Extract Interface “Extract Interface”重構能夠從一個已存在的類中提取接口,它可以從某個類中選擇方法,把這些方法提取到一個單獨的接口中。選擇提取接口的類,右鍵選擇Refactor菜單的“Extract Interface”菜單項,打開“Extract Interface”對話框,如圖10所示。
圖10 “Extract Interface”對話框
單元OK按鈕,將會提取TestInterface的接口,提取接口后,當前選擇的類將會實現此接口。 提示:只有公用方法才可以被提取為接口的方法。 4. Generalize Declared Type “Generalize Declared Type”重構能夠改變變量、參數、屬性以及函數的返回值的類型,可以把這些類型改成其父類的類型。在Refactor菜單中選擇“Generalize Declared Type”,如圖11所示。
圖11 “Generalize Declared Type”對話框
單擊OK按鈕,能夠把聲明的類型改成當對話框中選擇的類型。 5. User Supertype Where Possible “User Supertype Where Possible”重構能夠用某一個類的父類的類型替換當前類的類型,選擇需要被替換引用的類。在Refactor菜單中選擇“User Supertype Where Possible”打開“User Supertype Where Possible”對話框,如圖12所示。
圖12 “User Supertype Where Possible”對話框
“Generalize Declared Type”重構和“User Supertype Where Possible”重構在面向接口編程方面是很有用的,可以把引用的對象盡可能用接口進行實現。提示:“User Supertype Where Possible”重構將替換其它類中的引用,要想看到重構的效果,應該找到其它類引用的位置,此操作不會修改當前文件。
類內部重構 類內部重構有如下一些: 1. Inline “Inline”重構能用函數的內容替換掉函數的引用。首先選擇函數的引用,在Refactor菜單中選擇“Inline”打開“Inline”對話框,如圖13所示。
圖13 “Inline”對話框
單擊確定按鈕,Eclipse將會用方法實現的部分替換引用的部分,即當前不采用方法調用的方式進行操作。也可以選擇“All invocations”和“Delete method declaration”,Eclipse會替換掉所有引用方法的位置,并且刪除方法。提示:Inline會用方法的實現部分替換所有調用方法的地方。 2. Extract Method “Extract Method”重構和“Inline”重構相反,它能夠從冗長的方法中提取小的方法,把大的方法分解成多個小方法來實現,通過此重構能夠使代碼看上去更簡單漂亮,也很大程度上提高代碼的復用性。可以選擇要提取方法的代碼,在Refactor菜單中選擇“Extract Method”打開“Extract Method”對話框,如圖14所示。
圖14 “Extract Method”對話框
“Extract Method”重構是非常好的重構方式,能夠把大的方法體重構成多個方法的實現,使代碼更清楚易懂。提示:“Extract Method”重構和“Inline”重構是對應的,有些時候為了組織一些不合的函數,可以先通過“Inline”的方式生成一個大的函數,再通過“Extract Method”來重構大的函數,使代碼更趨于合理。3. Extract Local Variable 在開發過程中,使用變量代替表達式是非常好的,這樣能使代碼更容易被理解。Eclipse中可以通過“Extract Local Variable”重構實現提取局部的表達式。首先選擇表達式,在Refactor菜單中選擇“Extract Local Variable”打開“Extract Local Variable”對話框,如圖15所示。
圖15 “Extract Local Variable”對話框
4. Extract Constant “Extract Constant”重構和“Extract Local Variable”重構類似,它可以把表達式定義為常量,另外“Extract Constant”重構能夠設定常量的可見性。選擇表達式,在Refactor菜單中選擇“Extract Constant”打開“Extract Constant”對話框,如圖16所示。
圖16 “Extract Constant”對話框
5. Introduce Parameter “Introduce Parameter”重構可以通過函數中的表達式、變量或引用為函數添加新的參數,還能夠自動更新引用此函數的其它位置的默認參數。要想進行“Introduce Parameter”重構,可以選擇表達式、變量或引用。在Refactor菜單中選擇“Introduce Parameter”打開“Introduce Parameter”對話框,如圖17所示。
圖17 “Introduce Parameter”對話框
6. Introduce Factory “Introduce Factory”重構能夠為類創建工廠方法。首先選擇需要創建工廠方法的類的構造函數,在Refactor菜單中選擇“Introduce Factory”打開“Introduce Factory”對話框,如圖18所示。
圖18 “Introduce Factory”對話框
在“Introduce Factory”對話框中,可以輸入工廠方法的名字,以及工廠類,Eclipse將會自動根據構造函數創建工廠方法。 提示:工廠類應該已經存在,通常可以在一個工廠類中為多個關聯的類創建工廠方法,所以在使用“Introduce Factory”重構前,應該先創建好工廠類。 7. Convert Local Variable to Field “Convert Local Variable to Field”重構能夠把局部的變量轉換成類中的全局變量。首先選擇要轉換的局部變量,在Refactor菜單中選擇“Convert Local Variable to Field”打開“Convert Local Variable to Field”對話框,如圖19所示。
圖19 “Convert Local Variable to Field”對話框
在“Convert Local Variable to Field”對話框中,還能夠修改變量的名稱以及變量的可見性。 8. Encapsulate Field “Encapsulate Field”重構能夠包裝屬性的可訪問性,以及生成訪問的方法。首先選擇要包裝的屬性,在Refactor菜單中選擇“Encapsulate Field”打開“Encapsulate Field”對話框,如圖20所示。
圖20 “Encapsulate Field”對話框
通常通過“Encapsulate Field”可以生成get和set方法。在“Encapsulate Field”對話框中可以輸入屬性的訪問方法的名稱,以及方法生成的位置和方法的可見性。提示:通過右鍵菜單的Source菜單也能生成相應的get和set方法。 Undo and Redo Eclipse的自動重構功能能夠很好的支持各種程序元素的重命名,并自動更新相關的引用。Eclipse能夠支持方法、字段在類之間移動,并自動更新引用,較好地支持內聯字段、函數的更新替換,較好地支持抽取方法、變量等程序元素。重構的過程是一個不斷嘗試和探索的過程。Eclipse的重構支持撤銷和重做,并且能夠預覽重構結果,這些是很實用的功能。要想執行撤消和重做(Undo and Redo)的功能,可以直接按快捷鍵Ctrl+Z以及Ctrl+Y,也可以選擇Edit菜單的Undo和Redo操作。提示:雖然Eclipse對重構提供了很強大的支持,但是重構后代碼的測試是必不可少的,而且不能指望Eclipse能夠解決所有重構的問題,有些時候手動重構還是必須的。自動重構的理念應該是“工具輔助下的重構工作”,但開發人員仍然承擔很大一部分重構工作。
新聞熱點
疑難解答