什么是重构
在不改变代码外在行为的前提下,对代码做出修改以改进程序内部的结构
简单地说就是在代码写好后改进它的设计
谁该阅读这本书
- 专业程序员(能够提高你的代码质量)
- 资深设计师和架构规划师(理解为什么需要重构,哪里需要重构)
阅读技巧
带着疑问去读:
- 如果你想要知道重构是什么。第1章够了
- 如果你想要知道为什么要重构,第1,2章
- 如果你想知道该在什么地方重构,第3章
- 如果你想进行重构,第1,2,3,4章。并根据目录选读
第1章 重构,第一个案例
public String statement(){
double totalAmount=0;
int frequentRenterPoints=0;
Enumeration<Rental> rentals = _rentals.elements();
String result = "Rental Record for "+getName()+"\n";
while(rentals.hasMoreElements()){
double thisAmount=0;
Rental each = (Rental)rentals.nextElement();
switch (each.getMovie().getPriceCode()) {
case Movie.CHILDRENS:
thisAmount += 1.5;
if(each.getDaysRented()>3){
thisAmount += (each.getDaysRented()-3)*1.5;
}
break;
case Movie.NEW_RELEASE:
thisAmount += each.getDaysRented()*3;
break;
case Movie.REGULAR:
thisAmount += 2;
if(each.getDaysRented()>2){
thisAmount += (each.getDaysRented()-2)*1.5;
}
break;
default:
break;
}
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;
}
这是只是一个方法。直接评价:太复杂,复用率低
解决方法
分解并重组
将代码按照功能拆分。每个功能只做一件事。
拆除switch并封装为方法
private double amountFor(Rental each, double result) {
switch (each.getMovie().getPriceCode()) {
case Movie.CHILDRENS:
result += 1.5;
if(each.getDaysRented()>3){
result += (each.getDaysRented()-3)*1.5;
}
break;
case Movie.NEW_RELEASE:
result += each.getDaysRented()*3;
break;
case Movie.REGULAR:
result += 2;
if(each.getDaysRented()>2){
result += (each.getDaysRented()-2)*1.5;
}
break;
default:
break;
}
return result;
}
重设变量名
变量名必须保证简单清楚,不产生歧义。比如上方代码的each就是可修改的变量名。因为each到底指的是什么
搬移代码
因为这个方法只使用了rental的信息。所以需要放在rental类下
去除临时变量
statement方法中有两个临时变量totalAmount和frequentRenterPoints。 做成getTotalAmount和getFrequentRenterPoints方法
public String statement() {
double totalAmount = 0;
int frequentRenterPoints = 0;
// Enumeration接口定义了从一个数据结构得到连续数据的手段
Enumeration rentals = _rentals.elements();
String result = "Rental Record for" + getName() + "\n";
while (rentals.hasMoreElements()) {
Rental each = (Rental) rentals.nextElement();
result += "\t" + each.getMovie().getTitle() + "\t" + String.valueOf(each.getMovie().getPrice().getCharge(each.getDayRented())) + "\n";
}
result += "Amount owed is " + String.valueOf(getTotalCharge()) + "\n";
result += "You earned " + String.valueOf(getTotalFrequentRenterPoints()) + "frequent renter points";
return result;
}
private int getTotalFrequentRenterPoints(){
int result=0;
Enumeration rentals=_rentals.elements();
while(rentals.hasMoreElements()){
Rental each=(Rental)rentals.nextElement();
result+=each.getMovie().getFrequentRenterPoints(each.getDayRented());
}
return result;
}
private double getTotalCharge(){
double result =0;
Enumeration rentals=_rentals.elements();
while(rentals.hasMoreElements()){
Rental each=(Rental)rentals.nextElement();
result+=each.getMovie().getPrice().getCharge(each.getDayRented());
}
return result;
}
测试重构后的方法
保证重构后的方法能够满足所有的需求
总结
- 代码块俞小,代码的功能就俞容易管理,代码的处理和移动也就俞轻松
- 任何不会被修改的变量都可以被当成参数传入新的函数,至于会被修改的变量需要慎重。如果只有一个变量会被修改,可以把它当做返回值。
- 绝大多数情况下,函数应该放在它所使用的数据的所属对象内
- 最好不要在另一个对象的属性基础上运用switch语句。如果不得不使用,也应该在对象自己的数据上使用,而不是在别人的数据上使用。
- 使用继承来适当组织类关系后,可以用多态取代switch语句。