本文用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;
}
Java Asp PHP .Net XML C/C++ CGI VB Jsp J2ee J2se J2me EJB Servlet Tomcat Resin Struts Weblogic Eclipse ANT GUI JMS Web servise IDEA Webphere Hibernate Spring Jboss Applet Swing Socket Javamail Perl Ajax P2P 安全 模式 框架 测试 开源 游戏
Windows XP Windows 2000 Windows 2003 Windows Me Windows 9.x Linux UNIX 注册表 操作系统 服务器 应用服务器