Sung in Blog
一些技術文章 & 一些生活雜碎
BlogJava
首頁
新隨筆
新文章
管理
WebSite ConnecTionS
常用鏈接
我的隨筆
我的評論
我的參與
最新評論
隨筆檔案
2006年2月 (1)
2005年11月 (2)
2005年10月 (18)
文章分類
Amusement(2)
Database
Eclipse(14)
Everyday life(10)
FrameWork(2)
In GuangZhou
In NanJing
In Xi‘an(2)
Java(55)
Job & Interview(53)
Keep fits(1)
Linux(4)
Management(1)
Network(3)
software Development(17)
Struts(28)
Testing(2)
Thinking in Design(8)
Tomcat(18)
人間五十年(15)
唐詩宋詞(7)
天下美食(7)
嵌入式技術
數碼時尚(2)
積分與排名
積分 - 255030
排名 - 221
最新評論
1.?re: java對象序列化
很好,轉走了!謝謝!
--et2007
2.?re: Java的Web框架對比
<javascript>alert("略懂")</javascript>
--kara
3.?re: C# 與 C 、 C++ 、 D 、 Java 的性能比較[第一部分]
另外c++的流的性能也是爛到了一定境界
我用mmx優化過的讀取文件內整數字符串數組的性能是c++循環用流讀取的速度幾十倍
--zmy
4.?re: C# 與 C 、 C++ 、 D 、 Java 的性能比較[第一部分]
c#1.0 不值得拿來說
4.0速度會讓你滿意的
不知道5.0怎樣
--zmy
5.?re: C# 與 C 、 C++ 、 D 、 Java 的性能比較 [第二部分]
第一部分呢
--糖糖
閱讀排行榜
1.?一些騰訊筆試題目 (16831)
2.?[強烈推薦]IBM公司面試題(附答案)——病狗問題(7527)
3.?2005各大公司薪水民間版本(4908)
4.?Java的Web框架對比 (3805)
5.?用微軟試題膨脹你的思維(3347)
評論排行榜
1.?[強烈推薦]IBM公司面試題(附答案)——病狗問題(19)
2.?一些騰訊筆試題目 (17)
3.?Java的Web框架對比 (8)
4.?2005各大公司薪水民間版本(4)
5.?Software Engineer -2006 Graduate Intake(HP2006校園招聘)(2)
Eclipse中自動重構實現探索
本文用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()
目的:
1、 把statement()函數中的swich語句提煉到獨立的函數amountFor()中。
2、 修改amountFor()參數命名
重構方法:
Extract Method
Rename Method
方法:
1、選中swich語句的代碼塊,在右鍵菜單中選擇"重構/抽取方法",出現參數對話框。Eclipse自動分析代碼塊中的局部變量,找到了兩個局部變量:each和thisAmount。其中,each只是在代碼塊中被讀取,但thisAmount會在代碼塊中被修改。按照重構Extract Method總結出來的規則,應該把each當作抽取函數的參數、thisAmount當作抽取函數的返回值。然而Eclipse并不做區分,直接把這兩個變量當作抽取新方法的參數,如圖。
我們的目的是把在抽取函數中不會被修改的each作為參數;會被修改的thisAmount作為返回值。解決的辦法是,把 double thisAmount = 0; 這行代碼移到switch語句的上面,變成這樣:
double thisAmount = 0;
switch(each.getMovie().getPriceCode()){
case Movie.REGULAR:
thisAmount += 2;
if(each.getDaysRented()>2)
thisAmount += (each.getDaysRented()-2)*1.5;
break;
case Movie.NEW_RELEASE:
thisAmount += each.getDaysRented()*3;
break;
case Movie.CHILDRENS:
thisAmount += 1.5;
if(each.getDaysRented()>3)
thisAmount += (each.getDaysRented()-3)*1.5;
break;
}
選中這段代碼,在右鍵菜單中選擇"重構/抽取方法",eclipse這次變得聰明點了,如圖。
選擇"預覽"按鈕預先查看重構后的結果,符合我們最初的目的。
選擇"確定"按鈕,重構后的代碼片斷如下:
public String statement() {
double totalAmount = 0;
int frequentRenterPoints = 0;
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()>1)
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 = 0;
switch(each.getMovie().getPriceCode()){
case Movie.REGULAR:
thisAmount += 2;
if(each.getDaysRented()>2)
thisAmount += (each.getDaysRented()-2)*1.5;
break;
case Movie.NEW_RELEASE:
thisAmount += each.getDaysRented()*3;
break;
case Movie.CHILDRENS:
thisAmount += 1.5;
if(each.getDaysRented()>3)
thisAmount += (each.getDaysRented()-3)*1.5;
break;
}
return thisAmount;
}
2、選中amountFor()的參數each,在右鍵菜單中選擇"重構/重命名",在對話框中輸入新的名稱:aRental,選擇確定,amountFor()中所有each的引用全部被替換成新的名稱。用同樣的辦法修改amountFor()中的局部變量thisAmount為result。重構后的amountFor()代碼如下:
/**
* @param aRental
* @return
*/
private double amountFor(Rental aRental) {
double result = 0;
switch(aRental.getMovie().getPriceCode()){
case Movie.REGULAR:
result += 2;
if(aRental.getDaysRented()>2)
result += (aRental.getDaysRented()-2)*1.5;
break;
case Movie.NEW_RELEASE:
result += aRental.getDaysRented()*3;
break;
case Movie.CHILDRENS:
result += 1.5;
if(aRental.getDaysRented()>3)
result += (aRental.getDaysRented()-3)*1.5;
break;
}
return result;
}
三、重構第二步:搬移"金額計算"代碼
目的:
1、 將函數amountFor()轉移到Rental類中,并更名為getCharge()。
2、 更新并替換所有對amountFor()的引用。
重構方法:
Move Method
Change Method signatrue
Inline Method
Inline Temp
方法:
1、選中函數amountFor()的定義,在右鍵菜單中選擇"重構/移動",顯示參數設置對話框。把新方法名改成getCharge。按下"確定"按鈕,Customer Class中的amountFor()函數被移動到Rental Class中,并更名為:getCharge()。
同時eclipse自動在Customer的amountFor()函數中添加一行對新函數的"委托"代碼:
private double amountFor(Rental aRental) {
return aRental.getCharge();
}
這行代碼會產生編譯錯誤,原因是amountFor()的private型被傳遞到了新的方法中:
/**
* @param this
* @return
*/
private double getCharge() {
……
}
2、繼續重構!選中getCharge()方法,在右鍵菜單中選擇"重構/更改方法特征符",彈出參數選擇對話框,把訪問修飾符從private改成public。Eclipse的編譯錯誤提示自動消失。
3、回到Customer類,把所有對amountFor()引用的地方替換成直接對getCharge()的引用。選中Customer類的函數amountFor(Rental aRental),在右鍵菜單中選擇"重構/內聯",出現參數選擇對話框。
選擇"確認"按鈕,引用amountFor()的地方被替換成對getCharge()的引用。
public String statement() {
……
double thisAmount = each.getCharge();
……
}
4、除去臨時變量thisAmount。
選中變量thisAmount,在右鍵菜單中選擇"重構/內聯",重構預覽窗口如下,可見達到了重構的目的。按下"確認"按鈕重構代碼。
statement()代碼:
public String statement() {
double totalAmount = 0; // 總消費金額
int frequentRenterPoints = 0; // 常客積點
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()>1)
frequentRenterPoints ++;
// show figures for this rental(顯示此筆租借數據)
result += "\t" + each.getMovie().getTitle() + "\t" +
String.valueOf(each.getCharge()) + "\n";
totalAmount += each.getCharge();
}
// add footer lines(結尾打印)
result += "Amount owed is " + String.valueOf(totalAmount) + "\n";
result += "You earned " + String.valueOf(frequentRenterPoints) + " frequent renter points";
return result;
}
四、重構第三步:提煉"常客積點計算"代碼
目的:提取"常客積點計算"代碼并放在Rental類中,"常客積點計算"代碼如下。
public String statement() {
……
// add frequent renter points
frequentRenterPoints ++;
// add bouns for a two day new release rental
if((each.getMovie().getPriceCode())==Movie.NEW_RELEASE && each.getDaysRented()>1)
frequentRenterPoints ++;
……
}
重構后的代碼如下:
frequentRenterPoints += each.getFrequentRenterPoints();
重構方法:
Extract Method
Move Method
Change Method signatrue
Inline Method
方法:
1、 首先,抽取代碼到獨立的函數中。
用"抽取方法"重構代碼,函數名:getFrequentRenterPoints。很遺憾,eclipse的不能生成諸如:frequentRenterPoints += getFrequentRenterPoints(Rental aRental); 的代碼。原因是執行自增操作的局部變量frequentRenterPoints要出現在等式右邊,因此抽取函數getFrequentRenterPoints()一定要把frequentRenterPoints作為參數。手工修改函數和對函數的引用,重構后的代碼如下:
public String statement() {
……
while(rentals.hasMoreElements()){
……
frequentRenterPoints += getFrequentRenterPoints(each);
……
}
……
}
/**
* @param each
* @return
*/
private int getFrequentRenterPoints(Rental each) {
if((each.getMovie().getPriceCode())==Movie.NEW_RELEASE && each.getDaysRented()>1)
return 2;
else
return 1;
}
2、 把getFrequentRenterPoints()移動到Rental類中。
3、 對getFrequentRenterPoints()"更改方法特征符"為public。
4、 對Customer的函數getFrequentRenterPoints()執行內聯操作,重構目標完成。
五、重構第四步:去除臨時變量(totalAmount和frequentRenterPoints)
目的:去除臨時變量(totalAmount和frequentRenterPoints)
方法:
1、 分析totalAmount和frequentRenterPoints的定義和引用結構如下:
// 聲明和定義
double totalAmount = 0;
int frequentRenterPoints = 0;
……
// 在循環中修改
while(rentals.hasMoreElements()){
……
frequentRenterPoints += each.getFrequentRenterPoints();
……
totalAmount += each.getCharge();
……
}
……
// 在循環外使用
result += "Amount owed is " + String.valueOf(totalAmount) + "\n";
result += "You earned " + String.valueOf(frequentRenterPoints) + " frequent renter points";
……
上述兩個變量在循環體外面定義和使用,在循環中被修改,運用Replace Temp with Query方法去除這兩個臨時變量是一項稍微復雜的重構。很遺憾,eclipse目前不支持這樣的重構。
2、手工修改代碼。
六、重構第五步:運用多態取代與價格相關的條件邏輯
目的:
1、 把Rental類中的函數getCharge()移動到Movie類中。
2、 把Rental類中的函數getFrequentRenterPoints()移動到Movie類中。
重構方法:
Move Method
Inline Method
方法:
1、 選中Rental類中的函數getCharge(),右鍵菜單選中"重構/移動",eclipse提示找不到接收者,不能移動。原因在于這行語句:
switch(getMovie().getPriceCode()){//取得影片出租價格
選中getMovie(),右鍵菜單選中"重構/內聯",確定后代碼成為:
switch(_movie.getPriceCode()){ //取得影片出租價格
選中getCharge(),執行"重構/移動"后,函數被移動到Movie類中。然而這只是部分達成了重構目的,我們發現,移動后的代碼把Rental作為參數傳給了getCharge(),手工修改一下,代碼變成:
class Movie ……
/**
* @param this
* @return
*/
public double getCharge(int _daysRented) {
double result = 0;
switch(getPriceCode()){ //取得影片出租價格
case Movie.REGULAR: // 普通片
result += 2;
if(_daysRented>2)
result += (_daysRented-2)*1.5;
break;
case Movie.NEW_RELEASE: // 新片
result += _daysRented*3;
break;
case Movie.CHILDRENS: // 兒童片
result += 1.5;
if(_daysRented>3)
result += (_daysRented-3)*1.5;
break;
}
return result;
}
class Rental……
/**
* @param this
* @return
*/
public double getCharge() {
return _movie.getCharge(_daysRented);
}
2、用同樣的步驟處理getFrequentRenterPoints(),重構后的代碼:
class Movie ……
/**
* @param frequentRenterPoints
* @param this
* @return
*/
public int getFrequentRenterPoints(int daysRented) {
if((getPriceCode())==Movie.NEW_RELEASE && daysRented>1)
return 2;
else
return 1;
}
class Rental……
/**
* @param frequentRenterPoints
* @param this
* @return
*/
public int getFrequentRenterPoints(int daysRented) {
if((getPriceCode())==Movie.NEW_RELEASE && daysRented>1)
return 2;
else
return 1;
}
七、重構第六步:終于……我們來到繼承
目的:對switch語句引入state模式。
方法:
很遺憾,不得不在這里提前結束eclipse的自動重構之旅。Eclipse幾乎不能做結構上的重構。也許Martin Fowler在書中呼喚的自動重構工具止于"工具輔助下的重構工作"這一理念。藝術是人類的專利,編程藝術的夢想將持續下去。
感興趣的讀者可以查看手工重構的最后一步代碼。將重構進行到底!
附錄:eclipse支持的重構方法(摘自eclipse中文幫助)
posted on 2005-11-02 15:42
Sung
閱讀(753)
評論(0)
編輯
收藏
所屬分類:
Eclipse
新用戶注冊
刷新評論列表
只有注冊用戶
登錄
后才能發表評論。
網站導航:
博客園
IT新聞
Chat2DB
C++博客
博問
相關文章:
Eclipse中自動重構實現探索
利用Eclipse編輯中文資源文件
Eclipse插件開發之添加簡單的GUI元素
Eclipse快速上手指南之使用ANT
在Eclipse中如何利用Maven
測試實踐:Eclipse 之 JUnit
利用Eclipse開發Hibernate應用程序
利用Myeclipse快速開發struts應用程序
Eclipse 3.0 簡介和插件開發示例
Eclipse的使用簡介及插件開發
Copyright ©2025 Sung Powered By
博客園
模板提供:
滬江博客
主站蜘蛛池模板:
亚洲中文字幕无码爆乳AV
|
国产又大又粗又硬又长免费
|
亚洲精品乱码久久久久久蜜桃不卡
|
亚洲偷自拍另类图片二区
|
大地资源在线观看免费高清
|
亚洲国产成a人v在线观看
|
中文字幕影片免费在线观看
|
亚洲1234区乱码
|
永久免费毛片在线播放
|
亚洲精品国产国语
|
国产又粗又猛又爽又黄的免费视频
|
国产特黄一级一片免费
|
色久悠悠婷婷综合在线亚洲
|
日本免费A级毛一片
|
亚洲高清视频在线观看
|
1000部啪啪毛片免费看
|
日韩亚洲国产高清免费视频
|
最近中文字幕免费mv视频8
|
亚洲国产成人无码AV在线
|
国产成人免费一区二区三区
|
国产精品午夜免费观看网站
|
久久久久无码精品亚洲日韩
|
久久成人免费播放网站
|
亚洲美女自拍视频
|
最新免费jlzzjlzz在线播放
|
无码色偷偷亚洲国内自拍
|
亚洲亚洲人成综合网络
|
一级毛片**不卡免费播
|
亚洲婷婷第一狠人综合精品
|
四虎永久成人免费
|
日本一区午夜艳熟免费
|
亚洲国产熟亚洲女视频
|
亚洲成A人片在线观看中文
|
久久久久久免费一区二区三区
|
亚洲男人天堂2017
|
免费观看黄网站在线播放
|
真人无码作爱免费视频
|
亚洲Av无码专区国产乱码DVD
|
国产乱码免费卡1卡二卡3卡
|
深夜久久AAAAA级毛片免费看
|
人人狠狠综合久久亚洲88
|