编写的4个测试用例全部通过了,但是如果想对迭代器中每一个元素的计算结果进行验证现有的函数就无法完成要求了。
下面运用重构中的抽取函数分解Customer类中超长的方法statement(),这个函数完成的功能有些无所不包了。既计算各个租借的费用和常客积点,又要计算客户需要交纳的费用总和,最后还要输出报表的表头和表尾。另外,现有的快速设计对变化的应对不足,客户的电影分类可能会出现变化,如果去掉儿童片加上科幻片、故事片怎么办,客户的收费规则发生变化了怎么办。根据前面所说,他的味道不是一般的臭。
第一步:抽取函数
我们先把statement计算各个租借的费用的职责分离到一个单独的函数中。
private double amountFor(Rental rental){
double thisAmount=0.0;
switch (rental.getMovie().getPriceCode()) {
case Movie.REGULAR:
thisAmount+=2;
if(rental.getDayRented()>2)
thisAmount+=(rental.getDayRented()-2)*1.5;
break;
case Movie.CHILDRENS:
thisAmount+=1.5;
if(rental.getDayRented()>3)
thisAmount+=(rental.getDayRented()-3)*1.5;
break;
case Movie.NEW_RELEASE:
thisAmount+=(rental.getDayRented())*3;
break;
}
return thisAmount;
}
把原来的计算各个租借费用的部分注释起来,把private double thisAmount=0.0; 一句改为double thisAmount=amountFor(each);再次运行测试用例,和第一次的结果对比
我们通过人工比对输出结果,输出结果相同。我们稍后会通过测试用例由套件自动比对。
如果这一步我们的测试fail掉了,由于我们只走了很小的一步,因此我们可以轻易的退回去。记住,我们始终保持测试-〉编码(重构)-〉测试的步骤,小步快走的稳定节奏。
测试全部通过后,我们可以果断的删除刚才留在statement函数中我们注释过的临时代码。
分析我们刚刚抽取出来的amountFor函数,它只使用了rental类中的成员变量,放在Customer
类中并不合适,因此我们继续重构,把amountfor函数移动到Rental类中。
这里我们使用了集成开发环境中的功能:
这里我们看到eclipse自动侦测出目标的类为rental,我们把新的函数改名为getcharge,并勾选在原类型中创建委托,可以点击预览按钮察看重构结果,最后的结果如下:
Rental 类中:
double getCharge(){
double thisAmount=0.0;
switch (getMovie().getPriceCode()) {
case Movie.REGULAR:
thisAmount+=2;
if(getDayRented()>2)
thisAmount+=(getDayRented()-2)*1.5;
break;
case Movie.CHILDRENS:
thisAmount+=1.5;
if(getDayRented()>3)
thisAmount+=(getDayRented()-3)*1.5;
break;
case Movie.NEW_RELEASE:
thisAmount+=(getDayRented())*3;
break;
}
return thisAmount;
}
可以看到比amountfor函数更加简化。
Customer类中:
private double amountFor(Rental rental){
return rental.getCharge();
}
保留了一个rental示例的委托调用。
再次运行测试用例,通过。
我们继续前进!
我们回到Customer类中,察看Statement函数中的thisAmount变量,我们发现他的结果并没有发生变化,我们可以通过重构把这个无用的临时变量除去。直接把thisamount=amountFor(each);的语句右侧考到totalAmount+=thisAmount;语句右侧,编译器会提示thisamount变量并未使用,直接双击quick fix 除去这个变量。再次运行测试用例,通过。
下一步我们把计算常客积点的职责也从statement函数中分离出来:
我们添加函数
private int getFrequentCount(Rental rental){
if (rental.getMovie().getPriceCode()==Movie.NEW_RELEASE&&rental.getDayRented()>1)
return 2;
else return 1;
}
再把计算常客积点的部分改写如下:
frequentCount+=getFrequentCount(each);
测试后我们发现常客积点的计算和类Rental的关系更密切,我们再次移动getFrequentCount到rental类中。测试通过后,计算常客积点的部分改写如下:
frequentCount+= each.getFrequentCount();
接下来,我们把statement最后的两个临时变量frequentCount、totalAmount也通过替换为函数查询消除掉。
添加函数
private double getTotalCharge(){
double result=0.0;
Enumeration rental=rentals.elements();
while (rental.hasMoreElements()) {
Rental rent = (Rental) rental.nextElement();
result+=rent.getCharge();
}
return result;
}
把临时变量totalAmount替换为getTotalCharge()
添加函数
private int getTotalFrequentCount(){
int result=0;
Enumeration rental=rentals.elements();
while (rental.hasMoreElements()) {
Rental each = (Rental) rental.nextElement();
result+=each.getFrequentCount();
}
return result;
}
把临时变量frequentCount替换为getTotalFrequentCount
相应的,我们在测试中添加以下四条测试用例:
public void testGetCharge(){
Enumeration rentals=tom.getRentals().elements();
while (rentals.hasMoreElements()) {
Rental each = (Rental) rentals.nextElement();
switch(each.getMovie().getPriceCode()){
case Movie.CHILDRENS:
assertEquals(1.5,each.getCharge(),.1);
break;
case Movie.NEW_RELEASE:
assertEquals(15,each.getCharge(),.1);
break;
case Movie.REGULAR:
assertEquals(6.5,each.getCharge(),.1);
break;
}
}
}
public void testgetFrequentCount(){
Enumeration rentals=tom.getRentals().elements();
while (rentals.hasMoreElements()) {
Rental each = (Rental) rentals.nextElement();
switch(each.getMovie().getPriceCode()){
case Movie.CHILDRENS:
assertEquals(1,each.getFrequentCount(),.1);
break;
case Movie.NEW_RELEASE:
assertEquals(2,each.getFrequentCount(),.1);
break;
case Movie.REGULAR:
assertEquals(1,each.getFrequentCount(),.1);
break;
}
}
}
public void testGetTotalCharge(){
assertEquals(23,tom.getTotalCharge(),.1);
}
public void testGetTotalFrequentCount(){
assertEquals(4,tom.getTotalFrequentCount(),.1);
}
现在我们就可以对迭代器中的每个元素的结果进行验证了。