1.3.4 练习:重构常客积分计算
public String statement2() {
double totalAmount = 0;
int frequentRenterPoints = 0;
Iterator<Rental> rentalIter = rentals.iterator();
String result = "Rental Record for " + getName() + "\n";
while (rentalIter.hasNext()) {
double thisAmount = 0;
Rental each = rentalIter.next();
// determine amounts for each line(确定每行的金额)
thisAmount = each.getCharge();
// add frequent renter points(增加常客的积分)
frequentRenterPoints++;
// add bonus for a two day new release rental(为两天的新释放租金增加奖金)
// 积分根据是否是新片有所不同
if ((each.getMovie().getPriceCode() == Movie.NEWRELEASE)
&& each.getDayRented() > 1)
frequentRenterPoints++;
// show figures for the rental(显示租金的数字)
result += each.getMovie().getTitle() + "\t\t"
+ String.valueOf(thisAmount) + "\n";
totalAmount += thisAmount;
}
// add footer lines(添加页脚行)
result += "Amount owed is " + String.valueOf(totalAmount) + "\n";
result += "You earned " + String.valueOf(frequentRenterPoints)
+ " frequent renter points";
return result;
}
下面我们的关注点可以放在frequentRenterPoints这个变量上,好好思考一下应该怎么去重构这一部分。
-
提炼函数
就像之前那样,找到局部变量与参数。
很明显,each和frequentRenterPoints。
所以我们可以提炼出一个新的函数:getFrequentRenterPoints()。
-
搬移函数
就和之前一样,提炼出的方法和Customer没有什么关联,也需要放到Rental类中。
-
以查询取代临时变量
注意!和之前不一样的是,每循环一次,frequentRenterPoints都是累加上一次的值,也就是说,它并不是一个‘常量’。此处我们可以直接把新函数的返回值累加上去即可。
下面是更改后的代码:
public String statement2() {
double totalAmount = 0;
int frequentRenterPoints = 0;
Iterator<Rental> rentalIter = rentals.iterator();
String result = "Rental Record for " + getName() + "\n";
while (rentalIter.hasNext()) {
Rental each = rentalIter.next();
frequentRenterPoints += each.getFrequentRenterPoint();
// show figures for the rental(显示租金的数字)
result += each.getMovie().getTitle() + "\t\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类中的方法
/**
* 计算常客积分
*
* @author newre
* @return
*/
public int getFrequentRenterPoint() {
int frequentRenterPoints = 0;
frequentRenterPoints++;
if ((this.getMovie().getPriceCode() == Movie.NEWRELEASE)
&& this.getDayRented() > 1)
frequentRenterPoints++;
return frequentRenterPoints;
}
简化到了这里,getFrequentRenterPoint()其实还不够精炼——frequentRenterPoints太碍眼了,能不能去掉呢?
public int getFrequentRenterPoint() {
if ((this.getMovie().getPriceCode() == Movie.NEWRELEASE)
&& this.getDayRented() > 1)
return 2;
else
return 1;
}
此时,类图又一次发生了更改
/'在线作图(UML)网址:
http://www.plantuml.com/plantuml/uml/SyfFKj2rKt3CoKnELR1Io4ZDoSa70000
如果要修改的的话,打开网址后,直接复制上图片链接(或者粘贴下方代码)修改即可'/
@startuml
Title "影片出租店程序"
class Movie{
- private int priceCode
}
class Rental{
- private int daysRented
+ public getCharge()
+ public getFrequentRenterPoint()
}
class Customer{
+ public statement()
}
Rental --> Movie
Customer --> Rental
@enduml
1.3.5 练习:去除临时变量
消灭临时变量本身并不困难,困难的是,你要克服自己的认知——去吧,把代码‘复杂化’。
这一次练习的目的,是消灭totalAmount和frequentRenterPoints,如果打破天窗来说,其实很容易,难的是想到这样的方法——
新建一个方法吧,在方法内去遍历。
public String statement2() {
Iterator<Rental> rentalIter = rentals.iterator();
String result = "Rental Record for " + getName() + "\n";
while (rentalIter.hasNext()) {
Rental each = rentalIter.next();
// show figures for the rental(显示租金的数字)
result += each.getMovie().getTitle() + "\t\t"
+ String.valueOf(each.getCharge()) + "\n";
}
// add footer lines(添加页脚行)
result += "Amount owed is " + String.valueOf(getTotalCharge(rentals)) + "\n";
result += "You earned "
+ String.valueOf(getTotalFrequentRenterPoints(rentals))
+ " frequent renter points";
return result;
}
/**
* 获取总价
*
* @author newre
* @param rentals
* @return
*/
private double getTotalCharge(Vector<Rental> rentals) {
double result = 0;
Iterator<Rental> rentalIter = rentals.iterator();
while (rentalIter.hasNext()) {
Rental each = rentalIter.next();
result += each.getCharge();
}
return result;
}
/**
* 获取总积分
*
* @author newre
* @param rentals
* @return
*/
private int getTotalFrequentRenterPoints(Vector<Rental> rentals) {
int result = 0;
Iterator<Rental> rentalIter = rentals.iterator();
while (rentalIter.hasNext()) {
Rental each = rentalIter.next();
result += each.getFrequentRenterPoint();
}
return result;
}
记住,每进行一步,都要进行测试,养成习惯。(如果一时半会养成不了也没关系,摔得次数多了,也就会了)
再看看此时的类图。
/'在线作图(UML)网址:
http://www.plantuml.com/plantuml/uml/SyfFKj2rKt3CoKnELR1Io4ZDoSa70000
如果要修改的的话,打开网址后,直接复制上图片链接(或者粘贴下方代码)修改即可'/
@startuml
Title "影片出租店程序"
class Movie{
- private int priceCode
}
class Rental{
- private int daysRented
+ public getCharge()
+ public getFrequentRenterPoint()
}
class Customer{
+ public statement()
+ private getTotal()
+ private getTotalFrequentRenterPoints()
}
Rental --> Movie
Customer --> Rental
@enduml
1.3.6 最后小结
此时,单论statement()这个方法,已经变得极为简单,看看其中的内容我们可以发现,里面只剩下了字符串的拼接,这也正是我们重构的目的——职能划分。
回顾一下最开始的需求:
1、以HTML格式输出详单
在现在的代码结构下,我们仅需要新建一个方法,更改字符串拼接,至于里面方法逻辑,我们无需改动。
再看看第二个需求:
2、计费标准改变
amazing!我们只需更改Rental类中的getCharge()方法即可。
3、影片分类规则更改,具体改变方法据情况而定
a ha?情况而定?
现在,我们对statement()进行了重构,但显而易见的是,这样不够,不管是性能上,还是面对客户的需求上,我们还不能达到‘以不变应万变’的地步,我们只完成了第一个大步,剩余的路还需要走下去。