本文用eclipse的自动重构功能对一个程序实例进行重构目的是探索Eclipse自动重构可以在多大程度上辅助重构这个过程程序实例使用《RefactoringImproving the Design of Existing Code》一书中的例子
Eclipse的自动重构功能能够很好地支持各种程序元素的重命名并自动更新相关的引用Eclipse能够支持方法字段在类之间移动并自动更新引用Eclipse较好地支持内联字段函数的更新替换Eclipse较好地支持抽取方法变量等程序元素
重构的过程是一个不断尝试和探索的过程Eclipse的重构支持撤销和重做并且能够预览重构结果这些是很实用的功能
Eclipse的重命名抽取方法移动内联功能更改方法特征符等代码结构级别的重构方法是比较成熟同时也值得使用的功能至于设计结构上的重构eclipse还不能很好地支持但是作者相信自动重构的理念应该是工具辅助下的重构工作人仍然承担大部分重构工作
一预备工作
本文使用《RefactoringImproving the Design of Existing Code》一书第一章的例子重构前的代码及每一步重构后的代码见附件读者最好配合《RefactoringImproving 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(eachgetMovie()getPriceCode()){
case MovieREGULAR:
thisAmount += ;
if(eachgetDaysRented()>)
thisAmount += (eachgetDaysRented())*;
break;
case MovieNEW_RELEASE:
thisAmount += eachgetDaysRented()*;
break;
case MovieCHILDRENS:
thisAmount += ;
if(eachgetDaysRented()>)
thisAmount += (eachgetDaysRented())*;
break;
}
选中这段代码在右键菜单中选择重构/抽取方法eclipse这次变得聪明点了如图
educitycn/img_///jpg>
选择预览按钮预先查看重构后的结果符合我们最初的目的
educitycn/img_///jpg>
选择确定按钮重构后的代码片断如下
public String statement() {
double totalAmount = ;
int frequentRenterPoints = ;
Enumeration rentals = _rentalselements();
String result = Rental Record for + getName() + \n;
while(rentalshasMoreElements()){
Rental each = (Rental)rentalsnextElement();
double thisAmount = amountFor(each);
frequentRenterPoints ++;
if((eachgetMovie()getPriceCode())==MovieNEW_RELEASE &&eachgetDaysRented()>)
frequentRenterPoints ++;
result += \t + eachgetMovie()getTitle() + \t +StringvalueOf(thisAmount) + \n;
totalAmount += thisAmount;
}
result += Amount owed is + StringvalueOf(totalAmount) + \n;
result += You earned + StringvalueOf(frequentRenterPoints) + frequent renter points;
return result;
}
/**
* @param each
* @return
*/
private double amountFor(Rental each) {
double thisAmount = ;
switch(eachgetMovie()getPriceCode()){
case MovieREGULAR:
thisAmount += ;
if(eachgetDaysRented()>)
thisAmount += (eachgetDaysRented())*;
break;
case MovieNEW_RELEASE:
thisAmount += eachgetDaysRented()*;
break;
case MovieCHILDRENS:
thisAmount += ;
if(eachgetDaysRented()>)
thisAmount += (eachgetDaysRented())*;
break;
}
return thisAmount;
}
选中amountFor()的参数each在右键菜单中选择重构/重命名在对话框中输入新的名称aRental选择确定amountFor()中所有each的引用全部被替换成新的名称用同样的办法修改amountFor()中的局部变量thisAmount为result重构后的amountFor()代码如下
/**
* @param aRental
* @return
*/
private double amountFor(Rental aRental) {
double result = ;
switch(aRentalgetMovie()getPriceCode()){
case MovieREGULAR:
result += ;
if(aRentalgetDaysRented()>)
result += (aRentalgetDaysRented())*;
break;
case MovieNEW_RELEASE:
result += aRentalgetDaysRented()*;
break;
case MovieCHILDRENS:
result += ;
if(aRentalgetDaysRented()>)
result += (aRentalgetDaysRented())*;
break;
}
return result;
}
三重构第二步搬移金额计算代码
目的
将函数amountFor()转移到Rental类中并更名为getCharge()
更新并替换所有对amountFor()的引用
重构方法
Move Method
Change Method signatrue
Inline Method
Inline Temp
方法
选中函数amountFor()的定义在右键菜单中选择重构/移动显示参数设置对话框把新方法名改成getCharge按下确定按钮Customer Class中的amountFor()函数被移动到Rental Class中并更名为getCharge()
educitycn/img_///jpg>
同时eclipse自动在Customer的amountFor()函数中添加一行对新函数的委托代码
private double amountFor(Rental aRental) {
return aRentalgetCharge();
}
这行代码会产生编译错误原因是amountFor()的private型被传递到了新的方法中
/**
* @param this
* @return
*/
private double getCharge() {
……
}
继续重构!选中getCharge()方法在右键菜单中选择重构/更改方法特征符弹出参数选择对话框把访问修饰符从private改成publicEclipse的编译错误提示自动消失
educitycn/img_///jpg>
回到Customer类把所有对amountFor()引用的地方替换成直接对getCharge()的引用选中Customer类的函数amountFor(Rental aRental)在右键菜单中选择重构/内联出现参数选择对话框
educitycn/img_///jpg>
选择确认按钮引用amountFor()的地方被替换成对getCharge()的引用
public String statement() {
……
double thisAmount = eachgetCharge();
……
}
除去临时变量thisAmount
选中变量thisAmount在右键菜单中选择重构/内联重构预览窗口如下可见达到了重构的目的按下确认按钮重构代码
educitycn/img_///jpg>
statement()代码
public String statement() {
double totalAmount = ; // 总消费金额
int frequentRenterPoints = ; // 常客积点
Enumeration rentals = _rentalselements();
String result = Rental Record for + getName() + \n;
while(rentalshasMoreElements()){
Rental each = (Rental)rentalsnextElement(); //取得一笔租借记录
// add frequent renter points(累加 常客积点)
frequentRenterPoints ++;
// add bouns for a two day new release rental
if((eachgetMovie()getPriceCode())==MovieNEW_RELEASE && eachgetDaysRented()>)
frequentRenterPoints ++;
// show figures for this