重构小提示:重构技术系以微小的步伐修改程序。如果你犯下错误,很容易便可发现它。
上一篇文章:java 代码重构-第一章(分解并重组statement())
下一篇文章:java 代码重构-第一章(提炼代码)
上次重构了statement(),类看起来,感觉舒服了很多是吧?那下面我们再一步步的做下去...
现在,我已经把原本的函数分为两块,可以分别处理它们。我不喜欢amountFor() 内的某些变量名称,现在是修改它们的时候。
下面是原本的代码。
private int amountFor(Rental each) { // 计算一笔租片费。
int 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;
}
下面是易名后的代码:
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;
}
下面是重构后,整个类的代码
package com.mkfree.refactoring.shap1;
import java.util.Enumeration;
import java.util.Vector;
/**
* 顾客
*
* @author hk
*
* 2012-12-25 下午10:59:03
*/
public class Customer {
private String name;
private Vector<Rental> rentals = new Vector<>();
public Customer(String name) {
this.name = name;
}
/**
* 添加
*
* @param rental
*/
public void addRentals(Rental rental) {
rentals.add(rental);
}
public String getName() {
return name;
}
/**
* 通计清单
*
* @return
*/
public String statement() {
double totalAmount = 0;// 合计
int frequentRentePoints = 0;
Enumeration<Rental> enu_rentals = rentals.elements();
String result = "Rental Record for " + this.getName() + " \n";
while (enu_rentals.hasMoreElements()) {
double thisAmount = 0;
Rental each = enu_rentals.nextElement();
thisAmount = amountFor(each);// 计算一笔租片费
frequentRentePoints++;
if ((each.getMovie().getPriceCode() == Movie.NEW_RELEASE) && each.getDaysRented() > 1) {
frequentRentePoints++;
}
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(frequentRentePoints) + " frequent renter points";
return result;
}
/**
* 把计算一笔租片
*
* @param each
* @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;
}
}
易名之后我需要重新编译并测试,确保没有破坏任何东西。
更改变量名称是值得的行为吗?绝对值得。好的代码应该清楚表达出自己的功能,变量名称是代码清晰的关键。如果为了提高代码的清晰度,需要修改某些东西的名字,大胆去做吧。
重构小提示:任何一个傻瓜都能写出计算机可以理解的代码。惟有写出人类容易理解的代码,才是优秀的程序员。
代码应该表现自己的目的,这一点非常重要。阅读代码的时候,我经常进行重构。这样,随着对程序的理解逐渐加深,我也就不断地把这些理解嵌入代码中,这么一来才不会遗忘我曾经理解的东西。
搬移「金额计算」代码
观察amountFor() 时,我发现这个函数使用了来自Rental class 的信息,却没有使 用来自Customer class 的信息。
class Customer...
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;
}
这立刻使我怀疑它是否被放错了位置。绝大多数情况下,函数应该放在它所使用的数据的所属object(或说class)内,所以amountFor() 应该移到Rental class 去。为了这么做,我要运用Move Method。首先把代码拷贝到Rental class 内, 调整代码使之适应新家,然后重新编译。像下面这样。
class Rental...
double getCharge() {
double result = 0;
switch (getMovie().getPriceCode()) {
case Movie.REGULAR:
result += 2;
if (getDaysRented() > 2)
result += (getDaysRented() - 2) * 1.5;
break;
case Movie.NEW_RELEASE:
result += getDaysRented() * 3;
break;
case Movie.CHILDRENS:
result += 1.5;
if (getDaysRented() > 3)
result += (getDaysRented() - 3) * 1.5;
break;
}
return result;
}
在这个例子里,「适应新家」意味去掉参数。此外,我还要在搬移的同时变更函数名称。
现在我可以测试新函数是否正常工作。只要改变Customer.amountFor() 函数内容,使它委托(delegate)新函数即可。
class Customer...
private double amountFor(Rental aRental) {
return aRental.getCharge();
}
好了下面是三个类的最终代码
Rental 类
package com.mkfree.refactoring.shap1;
/**
* 租凭
*
* @author hk
*
* 2012-12-25 下午10:57:00
*/
public class Rental {
private Movie movie;
private int daysRented;
public Rental(Movie movie, int daysRented) {
this.movie = movie;
this.daysRented = daysRented;
}
public Movie getMovie() {
return movie;
}
public int getDaysRented() {
return daysRented;
}
double getCharge() {
double result = 0;
switch (getMovie().getPriceCode()) {
case Movie.REGULAR:
result += 2;
if (getDaysRented() > 2)
result += (getDaysRented() - 2) * 1.5;
break;
case Movie.NEW_RELEASE:
result += getDaysRented() * 3;
break;
case Movie.CHILDRENS:
result += 1.5;
if (getDaysRented() > 3)
result += (getDaysRented() - 3) * 1.5;
break;
}
return result;
}
}
Customer 类
package com.mkfree.refactoring.shap1;
import java.util.Enumeration;
import java.util.Vector;
/**
* 顾客
*
* @author hk
*
* 2012-12-25 下午10:59:03
*/
public class Customer {
private String name;
private Vector<Rental> rentals = new Vector<>();
public Customer(String name) {
this.name = name;
}
/**
* 添加
*
* @param rental
*/
public void addRentals(Rental rental) {
rentals.add(rental);
}
public String getName() {
return name;
}
/**
* 通计清单
*
* @return
*/
public String statement() {
double totalAmount = 0;// 合计
int frequentRentePoints = 0;
Enumeration<Rental> enu_rentals = rentals.elements();
String result = "Rental Record for " + this.getName() + " \n";
while (enu_rentals.hasMoreElements()) {
double thisAmount = 0;
Rental each = enu_rentals.nextElement();
thisAmount = each.getCharge();// 计算一笔租片费
frequentRentePoints++;
if ((each.getMovie().getPriceCode() == Movie.NEW_RELEASE) && each.getDaysRented() > 1) {
frequentRentePoints++;
}
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(frequentRentePoints) + " frequent renter points";
return result;
}
}
Movie 类
package com.mkfree.refactoring.shap1;
/**
* 电影类
* @author hk
*
* 2012-12-25 下午10:55:14
*/
public class Movie {
public static final int CHILDRENS = 2;
public static final int REGULAR = 0;
public static final int NEW_RELEASE = 1;
private String title;
private int priceCode;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public int getPriceCode() {
return priceCode;
}
public void setPriceCode(int priceCode) {
this.priceCode = priceCode;
}
}
现在我可以编译并测试,看看有没有破坏了什么东西。
做完这些修改之后(图1.3),下一件事就是去掉旧函数。编译器会告诉我是否我漏掉了什么。然后我进行测试,看看有没有破坏什么东西。