接1.3,本章对“常客积分计算”做类似处理。积分的计算视影片种类而有不同,不过不像收费规则有那么多变话。看来有理由把积分计算责任放在Rental类身上,首先需要针对“常客积分计算”这部分代码运用Extract Method(提炼函数)。
再来看局部变量。这里再一次用到了each,而它可以被当作参数传入新函数中。另一个临时变量是frequentRenterPoints。本例中,它在使用之前已经有初始值了,但是提炼出来的函数并没有读取该值,所以我们不需要将它当作参数传递进去,只需把新函数的返回值累加上去就可行了。
重构前的代码
public String statement(){
double totalAmount = 0;//总金额
int frequentRenterPoints = 0;//本次总积分
Enumeration<Rental> rentals = _rentals.elements();
// 租赁备案
String result = "Rental Record for "+getName()+"\n";
while(rentals.hasMoreElements()){
Rental each = rentals.nextElement();
// 常规积分累加
frequentRenterPoints++;
// 特殊新书积分计算
if (each.get_movie().get_priceCode() == Movie.NEW_RELEASE &&
each.get_dayRented() > 1) {
frequentRenterPoints++;
}
// 显示凭条
result += "\t"+each.get_movie().get_title()+"\t"+String.valueOf(each.getCharge())+"\n";
totalAmount += each.getCharge();
}
// 组装页脚
result += "Amount owed is "+String.valueOf(totalAmount)+"\n";
result += "You earned "+String.valueOf(frequentRenterPoints)+" frequent renter points";
return result;
}
重构后的代码
class Customer...
public String statement(){
double totalAmount = 0;//总金额
int frequentRenterPoints = 0;//本次总积分
Enumeration<Rental> rentals = _rentals.elements();
// 租赁备案
String result = "Rental Record for "+getName()+"\n";
while(rentals.hasMoreElements()){
Rental each = rentals.nextElement();
frequentRenterPoints += each.getFrequentRenterPoints();
// 显示凭条
result += "\t"+each.get_movie().get_title()+"\t"+String.valueOf(each.getCharge())+"\n";
totalAmount += each.getCharge();
}
// 组装页脚
result += "Amount owed is "+String.valueOf(totalAmount)+"\n";
result += "You earned "+String.valueOf(frequentRenterPoints)+" frequent renter points";
return result;
}
class Rental...
// 常客积分计算
public int getFrequentRenterPoints(){
if (get_movie().get_priceCode() == Movie.NEW_RELEASE &&
get_dayRented() > 1) {
return 2;
}else{
return 1;
}
}
最终的UML:
去除临时变量
正如我们之前说的那样,临时变量可能是个问题。它们只在自己所属的函数中有效,所以它们会助长冗长而复杂的函数。这里有两个临时变量,两者都是用来从Customer对象相关中的Rental对象中获取某个总量。不论哪个版本的都需要这些总量。我打算运用Replace Temp with Query(以查询代替临时变量),并利用查询函数(querymethod)来取代totalAmount和frequentRentalPoints这两个临时变量。由于类中的任何函数都可以调用上述查询函数,所以它能促成较干净的设计,而减少冗长复杂的函数。
首先用Customer类的getTotalCharge()取代totalAmount,由于totalAmount在循环内部被赋值,所以不得不把循环复制到查询函数中。
class Customer...
public String statement(){
int frequentRenterPoints = 0;//本次总积分
Enumeration<Rental> rentals = _rentals.elements();
// 租赁备案
String result = "Rental Record for "+getName()+"\n";
while(rentals.hasMoreElements()){
Rental each = rentals.nextElement();
frequentRenterPoints += each.getFrequentRenterPoints();
// 显示凭条
result += "\t"+each.get_movie().get_title()+"\t"+String.valueOf(each.getCharge())+"\n";
}
// 组装页脚
result += "Amount owed is "+String.valueOf(getTotalCharge())+"\n";
result += "You earned "+String.valueOf(frequentRenterPoints)+" frequent renter points";
return result;
}
// 计算总金额
public double getTotalCharge(){
double result = 0;
Enumeration<Rental> rentals = _rentals.elements();
while(rentals.hasMoreElements()){
Rental each = rentals.nextElement();
result += each.getCharge();
}
return result;
}
重构后,重新编译并测试,然后以同样的手法处理frequentRentalPints。
class Customer...
public String statement(){
Enumeration<Rental> rentals = _rentals.elements();
// 租赁备案
String result = "Rental Record for "+getName()+"\n";
while(rentals.hasMoreElements()){
Rental each = rentals.nextElement();
// 显示凭条
result += "\t"+each.get_movie().get_title()+"\t"+String.valueOf(each.getCharge())+"\n";
}
// 组装页脚
result += "Amount owed is "+String.valueOf(getTotalCharge())+"\n";
result += "You earned "+String.valueOf(getTotalFrequentRenterPoints())+" frequent renter points";
return result;
}
//计算总积分
public int getTotalFrequentRenterPoints(){
int result = 0;
Enumeration<Rental> rentals = _rentals.elements();
while(rentals.hasMoreElements()){
Rental each = rentals.nextElement();
result += each.getFrequentRenterPoints();
}
return result;
}
重新编译并测试
最终的UML