本文用eclipse的自動重構功能對一個程序實例進行重構
目的是探索Eclipse自動重構可以在多大程度上輔助重構這個過程
程序實例使用《Refactoring
Improving the Design of Existing Code》一書中的例子
Eclipse的自動重構功能能夠很好地支持各種程序元素的重命名
並自動更新相關的引用
Eclipse能夠支持方法
字段在類之間移動
並自動更新引用
Eclipse較好地支持內聯字段
函數的更新替換
Eclipse較好地支持抽取方法
變量等程序元素
重構的過程是一個不斷嘗試和探索的過程
Eclipse的重構支持撤銷和重做
並且能夠預覽重構結果
這些是很實用的功能
Eclipse的重命名
抽取方法
移動
內聯功能
更改方法特征符等代碼結構級別的重構方法
是比較成熟同時也值得使用的功能
至於設計結構上的重構
eclipse還不能很好地支持
但是作者相信
自動重構的理念應該是
工具輔助下的重構工作
人仍然承擔大部分重構工作
一預備工作 本文使用《Refactoring
Improving the Design of Existing Code》一書第一章的例子
重構前的代碼及每一步重構後的代碼見附件
讀者最好配合《Refactoring
Improving the Design of Existing Code》一書閱讀本文
Eclipse使用如下版本
同時安裝了中文語言包
二重構第一步分解並重組statement() 目的
把statement()函數中的swich語句提煉到獨立的函數amountFor()中
修改amountFor()參數命名
重構方法
Extract Method
Rename Method
方法
選中swich語句的代碼塊
在右鍵菜單中選擇
重構/抽取方法
出現參數對話框
Eclipse自動分析代碼塊中的局部變量
找到了兩個局部變量
each和thisAmount
其中
each只是在代碼塊中被讀取
但thisAmount會在代碼塊中被修改
按照重構Extract Method總結出來的規則
應該把each當作抽取函數的參數
thisAmount當作抽取函數的返回值
然而Eclipse並不做區分
直接把這兩個變量當作抽取新方法的參數
如圖
我們的目的是把在抽取函數中不會被修改的each作為參數
會被修改的thisAmount作為返回值
解決的辦法是
把 double thisAmount =
; 這行代碼移到switch語句的上面
變成這樣
double thisAmount =
;
switch(each
getMovie()
getPriceCode()){
case Movie
REGULAR:
thisAmount +=
;
if(each
getDaysRented()>
)
thisAmount += (each
getDaysRented()
)*
;
break;
case Movie
NEW_RELEASE:
thisAmount += each
getDaysRented()*
;
break;
case Movie
CHILDRENS:
thisAmount +=
;
if(each
getDaysRented()>
)
thisAmount += (each
getDaysRented()
)*
;
break;
}
選中這段代碼
在右鍵菜單中選擇
重構/抽取方法
eclipse這次變得聰明點了
如圖
educity
cn/img_
/
/
/
jpg>
選擇
預覽
按鈕預先查看重構後的結果
符合我們最初的目的
educity
cn/img_
/
/
/
jpg>
選擇
確定
按鈕
重構後的代碼片斷如下
public String statement() {
double totalAmount =
;
int frequentRenterPoints =
;
Enumeration rentals = _rentals
elements();
String result =
Rental Record for
+ getName() +
\n
;
while(rentals
hasMoreElements()){
Rental each = (Rental)rentals
nextElement();
double thisAmount = amountFor(each);
frequentRenterPoints ++;
if((each
getMovie()
getPriceCode())==Movie
NEW_RELEASE &&each
getDaysRented()>
)
frequentRenterPoints ++;
result +=
\t
+ each
getMovie()
getTitle() +
\t
+String
valueOf(thisAmount) +
\n
;
totalAmount += thisAmount;
}
result +=
Amount owed is
+ String
valueOf(totalAmount) +
\n
;
result +=
You earned
+ String
valueOf(frequentRenterPoints) +
frequent renter points
;
return result;
}
/**
* @param each
* @return
*/
private double amountFor(Rental each) {
double thisAmount =
;
switch(each
getMovie()
getPriceCode()){
case Movie
REGULAR:
thisAmount +=
;
if(each
getDaysRented()>
)
thisAmount += (each
getDaysRented()
)*
;
break;
case Movie
NEW_RELEASE:
thisAmount += each
getDaysRented()*
;
break;
case Movie
CHILDRENS:
thisAmount +=
;
if(each
getDaysRented()>
)
thisAmount += (each
getDaysRented()
)*
;
break;
}
return thisAmount;
}
選中amountFor()的參數each
在右鍵菜單中選擇
重構/重命名
在對話框中輸入新的名稱
aRental
選擇確定
amountFor()中所有each的引用全部被替換成新的名稱
用同樣的辦法修改amountFor()中的局部變量thisAmount為result
重構後的amountFor()代碼如下
/**
* @param aRental
* @return
*/
private double amountFor(Rental aRental) {
double result =
;
switch(aRental
getMovie()
getPriceCode()){
case Movie
REGULAR:
result +=
;
if(aRental
getDaysRented()>
)
result += (aRental
getDaysRented()
)*
;
break;
case Movie
NEW_RELEASE:
result += aRental
getDaysRented()*
;
break;
case Movie
CHILDRENS:
result +=
;
if(aRental
getDaysRented()>
)
result += (aRental
getDaysRented()
)*
;
break;
}
return result;
}
三重構第二步搬移金額計算代碼 目的
將函數amountFor()轉移到Rental類中
並更名為getCharge()
更新並替換所有對amountFor()的引用
重構方法
Move Method
Change Method signatrue
Inline Method
Inline Temp
方法
選中函數amountFor()的定義
在右鍵菜單中選擇
重構/移動
顯示參數設置對話框
把新方法名改成getCharge
按下
確定
按鈕
Customer Class中的amountFor()函數被移動到Rental Class中
並更名為
getCharge()
educity
cn/img_
/
/
/
jpg>
同時eclipse自動在Customer的amountFor()函數中添加一行對新函數的
委托
代碼
private double amountFor(Rental aRental) {
return aRental
getCharge();
}
這行代碼會產生編譯錯誤
原因是amountFor()的private型被傳遞到了新的方法中
/**
* @param this
* @return
*/
private double getCharge() {
……
}
繼續重構!選中getCharge()方法
在右鍵菜單中選擇
重構/更改方法特征符
彈出參數選擇對話框
把訪問修飾符從private改成public
Eclipse的編譯錯誤提示自動消失
educity
cn/img_
/
/
/
jpg>
回到Customer類
把所有對amountFor()引用的地方替換成直接對getCharge()的引用
選中Customer類的函數amountFor(Rental aRental)
在右鍵菜單中選擇
重構/內聯
出現參數選擇對話框
educity
cn/img_
/
/
/
jpg>
選擇
確認
按鈕
引用amountFor()的地方被替換成對getCharge()的引用
public String statement() {
……
double thisAmount = each
getCharge();
……
}
除去臨時變量thisAmount
選中變量thisAmount
在右鍵菜單中選擇
重構/內聯
重構預覽窗口如下
可見達到了重構的目的
按下
確認
按鈕重構代碼
educity
cn/img_
/
/
/
jpg>
statement()代碼
public String statement() {
double totalAmount =
; // 總消費金額
int frequentRenterPoints =
; // 常客積點
Enumeration rentals = _rentals
elements();
String result =
Rental Record for
+ getName() +
\n
;
while(rentals
hasMoreElements()){
Rental each = (Rental)rentals
nextElement(); //取得一筆租借記錄
// add frequent renter points(累加 常客積點)
frequentRenterPoints ++;
// add bouns for a two day new release rental
if((each
getMovie()
getPriceCode())==Movie
NEW_RELEASE && each
getDaysRented()>
)
frequentRenterPoints ++;
// show figures for this
From:http://tw.wingwit.com/Article/program/Java/ky/201311/11170.html