示例:影片出租店程序(重构——分解并重组Statement)
步骤:
1、提炼“金额计算”代码
1.1、提炼“逻辑泥团”——“提炼方法(Extract Method)”
Statement()中一个明显的“逻辑泥团”就是switch语句,把它提炼到独立函数“AmountFor”中。
Eclipse工具重构步骤:
(1)、选中代码
(2)、右键选择“Extract Method”
(3)、输入方法名“amountFor”
(4)、调整代码如下
public String statement() {
double totalAmount = 0; //--总消费金额
int frequentRenterPoints = 0; //--常客积点
Enumeration rentals = _rentals.elements();
String result = "Rental Record for " + get_name() + "/n";
while(rentals.hasMoreElements()){
double thisAmount = 0;
Rental each = (Rental) rentals.nextElement(); //--取得一笔租借记录
thisAmount = amountFor(each);
//---累加常客积点
frequentRenterPoints ++;
if (each.get_movie().get_priceCode() == Movie.NEW_RELEASE &&
each.get_daysRented() > 1)
frequentRenterPoints ++;
//---显示此笔租借数据
result += "/t" + each.get_movie().get_title() + "/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;
}
private double amountFor(Rental each) { //--计算一笔租片费用
double thisAmount = 0;
switch(each.get_movie().get_priceCode()){ //--取得影片出租价格
case Movie.REGULAR: //--普通片
thisAmount += 2;
if (each.get_daysRented() > 2)
thisAmount += (each.get_daysRented() - 2) * 1.5;
break;
case Movie.NEW_RELEASE: //--新片
thisAmount += each.get_daysRented() * 3;
break;
case Movie.CHILDRENS: //--儿童片
thisAmount += 1.5;
if (each.get_daysRented() > 3)
thisAmount += (each.get_daysRented() - 3) * 1.5;
break;
}
return thisAmount;
}
1.2、规范变量名称
好的代码应该清楚表达出自己的功能,变量名称是代码清浙的关键。
Eclipse工具重构步骤:
(1)、选中“变量名称”
(2)、右键选中“Rename”
(3)、输入新的“变量名称”
(4)、调整代码如下
private double amountFor(Rental aRental) { //--计算一笔租片费用
double result = 0;
switch(aRental.get_movie().get_priceCode()){ //--取得影片出租价格
case Movie.REGULAR: //--普通片
result += 2;
if (aRental.get_daysRented() > 2)
result += (aRental.get_daysRented() - 2) * 1.5;
break;
case Movie.NEW_RELEASE: //--新片
result += aRental.get_daysRented() * 3;
break;
case Movie.CHILDRENS: //--儿童片
result += 1.5;
if (aRental.get_daysRented() > 3)
result += (aRental.get_daysRented() - 3) * 1.5;
break;
}
return result;
}
1.3、搬移“金额计算”代码——“搬移方法(Move Method)”
AmountFor函数使用了来自Rental类的信息.却没有使用来自Customer类的信息。这表明它可能是被放错了位置,应将AmountFor()移到Rental类中。此外,还要在搬移的同时变更函数名称(AmountFor –> GetCharge)。
Eclipse工具重构步骤:
1、搬移方法
(1)、选中“方法名称”
(2)、右键选择“Move”
(3)、搬移方法
(4)、调整代码如下
package Movie_Ref;
/**
* 租赁
*
*/
public class Rental {
private Movie _movie; //影片
private int _daysRented; //租期
public Rental(Movie movie, int daysRented) {
_movie = movie;
_daysRented = daysRented;
}
public int get_daysRented() {
return _daysRented;
}
public Movie get_movie() {
return _movie;
}
double amountFor() { //--计算一笔租片费用
double result = 0;
switch(get_movie().get_priceCode()){ //--取得影片出租价格
case Movie.REGULAR: //--普通片
result += 2;
if (get_daysRented() > 2)
result += (get_daysRented() - 2) * 1.5;
break;
case Movie.NEW_RELEASE: //--新片
result += get_daysRented() * 3;
break;
case Movie.CHILDRENS: //--儿童片
result += 1.5;
if (get_daysRented() > 3)
result += (get_daysRented() - 3) * 1.5;
break;
}
return result;
}
}
2、变更函数名称
(1)、选中“函数名称”
(2)、右键选中“Rename”
(3)、输入新的“函数名称”
(4)、调整代码如下
double getCharge() { //--计算一笔租片费用
double result = 0;
switch(get_movie().get_priceCode()){ //--取得影片出租价格
case Movie.REGULAR: //--普通片
result += 2;
if (get_daysRented() > 2)
result += (get_daysRented() - 2) * 1.5;
break;
case Movie.NEW_RELEASE: //--新片
result += get_daysRented() * 3;
break;
case Movie.CHILDRENS: //--儿童片
result += 1.5;
if (get_daysRented() > 3)
result += (get_daysRented() - 3) * 1.5;
break;
}
return result;
}
1.4、删除临时变量——“替换临时变量(Replace Temp with Query)”
Statement方法中的临时变量thisAmount接受each.GetCharge的执行结果,然后就不再有任何改变。所以尽量除去这一类临时变量。
public String statement() {
double totalAmount = 0; //--总消费金额
int frequentRenterPoints = 0; //--常客积点
Enumeration rentals = _rentals.elements();
String result = "Rental Record for " + get_name() + "/n";
while(rentals.hasMoreElements()){
Rental each = (Rental) rentals.nextElement(); //--取得一笔租借记录
//---累加常客积点
frequentRenterPoints ++;
if (each.get_movie().get_priceCode() == Movie.NEW_RELEASE &&
each.get_daysRented() > 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;
}
2、提炼“常客积点计算”代码
2.1、提炼“常客积点计算”代码——“提炼函数(Extract Method)”
首先,我们需要针对“常客积点计算”这部分代码运用“提炼函数(Extract Method)”重构准则,将其提炼为函数“GetFrequentRenterPoints”。
public String statement() {
double totalAmount = 0; //--总消费金额
int frequentRenterPoints = 0; //--常客积点
Enumeration rentals = _rentals.elements();
String result = "Rental Record for " + get_name() + "/n";
while(rentals.hasMoreElements()){
Rental each = (Rental) rentals.nextElement(); //--取得一笔租借记录
//---累加常客积点
frequentRenterPoints = getFrequentRenterPoints(frequentRenterPoints, each);
//---显示此笔租借数据
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;
}
private int getFrequentRenterPoints(int frequentRenterPoints, Rental each) {
frequentRenterPoints ++;
if (each.get_movie().get_priceCode() == Movie.NEW_RELEASE &&
each.get_daysRented() > 1)
frequentRenterPoints ++;
return frequentRenterPoints;
}
然后,在“GetFrequentRenterPoints”函数内找到局部变量each,它可以被当作参数传入新函数中。另一个临时变量是frequentRenterPoints。本例中的它在被使用之前已经先有初值,但提炼出来的函数并没有读取该值,所以我们不需要将它当作参数传进去,只需对它执行“追加赋值操作“就行 。
public String statement() {
double totalAmount = 0; //--总消费金额
int frequentRenterPoints = 0; //--常客积点
Enumeration rentals = _rentals.elements();
String result = "Rental Record for " + get_name() + "/n";
while(rentals.hasMoreElements()){
Rental each = (Rental) rentals.nextElement(); //--取得一笔租借记录
//---累加常客积点
frequentRenterPoints += getFrequentRenterPoints(each);
//---显示此笔租借数据
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;
}
private int getFrequentRenterPoints(Rental each) {
int frequentRenterPoints = 0; //--常客积点
if (each.get_movie().get_priceCode() == Movie.NEW_RELEASE &&
each.get_daysRented() > 1)
frequentRenterPoints = 2;
else
frequentRenterPoints = 1;
return frequentRenterPoints;
}
2.2、规范变量名称
private int getFrequentRenterPoints(Rental each) {
if (each.get_movie().get_priceCode() == Movie.NEW_RELEASE &&
each.get_daysRented() > 1)
return 2;
else
return 1;
}
2.3、搬移“常客积点计算”代码——“搬移方法(Move Method)”
public class Customer {
…………
public String statement() {
double totalAmount = 0; //--总消费金额
int frequentRenterPoints = 0; //--常客积点
Enumeration rentals = _rentals.elements();
String result = "Rental Record for " + get_name() + "/n";
while(rentals.hasMoreElements()){
Rental each = (Rental) 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;
}
}
public class Rental {
…………
int getFrequentRenterPoints() {
if (get_movie().get_priceCode() == Movie.NEW_RELEASE &&
get_daysRented() > 1)
return 2;
else
return 1;
}
}
3、总量计算(去除临时变量)
statement函数中有两个临时变量totalAmount和frequentRenterPoints,两者都是用来从Customer对象相关的Rental对象中获得某个总量。因为不论 ASCll 版或 HTML 版都需要这些总量。所以,我们运用“替换临时变量(Replace Temp with Query)”和“查询方法(Query Method)”来取代临时变量。
通过去除临时变量,可以将冗长复杂的函数中的逻辑理顺,并使其更为清晰。如果系统中的其它地方需要这些信息,也可以很轻松地将“查询方法”加入Customer类的公共接口。
3.1、总消费金额
首先,使用“查询方法GetTotalCharge”去除“临时变量totalAmount”。
private double getTotalCharge() {
double totalAmount = 0; //--总消费金额
Enumeration rentals = _rentals.elements();
while(rentals.hasMoreElements()){
Rental each = (Rental) rentals.nextElement(); //--取得一笔租借记录
totalAmount += each.getCharge();
}
return totalAmount;
}
然后规范getTotalCharge方法中变量名称。
public class Customer {
…………
public String statement() {
int frequentRenterPoints = 0; //--常客积点
Enumeration rentals = _rentals.elements();
String result = "Rental Record for " + get_name() + "/n";
while(rentals.hasMoreElements()){
Rental each = (Rental) 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;
}
private double getTotalCharge() {
double result = 0; //--总消费金额
Enumeration rentals = _rentals.elements();
while(rentals.hasMoreElements()){
Rental each = (Rental) rentals.nextElement(); //--取得一笔租借记录
result += each.getCharge();
}
return result;
}
}
3.2、总常客积点
首先,用“查询方法GetTotalFrequentRenterPoints”去除“临时变量frequentRenterPoints”。
private int getTotalFrequentRenterPoints() {
int frequentRenterPoints = 0; //--常客积点
Enumeration rentals = _rentals.elements();
while(rentals.hasMoreElements()){
Rental each = (Rental) rentals.nextElement(); //--取得一笔租借记录
//---累加常客积点
frequentRenterPoints += each.getFrequentRenterPoints();
}
return frequentRenterPoints;
}
然后规范GetTotalFrequentRenterPoints方法中变量名称。
public class Customer {
…………
public String statement() {
Enumeration rentals = _rentals.elements();
String result = "Rental Record for " + get_name() + "/n";
while(rentals.hasMoreElements()){
Rental each = (Rental) 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;
}
private int getTotalFrequentRenterPoints() {
int result = 0; //--常客积点
Enumeration rentals = _rentals.elements();
while(rentals.hasMoreElements()){
Rental each = (Rental) rentals.nextElement(); //--取得一笔租借记录
//---累加常客积点
result += each.getFrequentRenterPoints();
}
return result;
}
…………
}
代码:
1、Customer.java
package Movie_Ref;
import java.util.Enumeration;
import java.util.Vector;
public class Customer {
private String _name;//姓名
private Vector _rentals = new Vector(); //租借记录
public Customer(String name) {
_name = name;
}
public void addRental(Rental obj) {
_rentals.addElement(obj);
}
public String get_name() {
return _name;
}
public String statement() {
Enumeration rentals = _rentals.elements();
String result = "Rental Record for " + get_name() + "/n";
while(rentals.hasMoreElements()){
Rental each = (Rental) 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;
}
private int getTotalFrequentRenterPoints() {
int result = 0; //--常客积点
Enumeration rentals = _rentals.elements();
while(rentals.hasMoreElements()){
Rental each = (Rental) rentals.nextElement(); //--取得一笔租借记录
//---累加常客积点
result += each.getFrequentRenterPoints();
}
return result;
}
private double getTotalCharge() {
double result = 0; //--总消费金额
Enumeration rentals = _rentals.elements();
while(rentals.hasMoreElements()){
Rental each = (Rental) rentals.nextElement(); //--取得一笔租借记录
result += each.getCharge();
}
return result;
}
}
2、Movie.java
package Movie_Ref;
/**
* 影片
*
*/
public class Movie {
public static final int REGULAR = 0;
public static final int NEW_RELEASE = 1;
public static final int CHILDRENS = 2;
private String _title; //名称
private int _priceCode; //价格(代号)
public Movie(String title, int priceCode) {
_title = title;
_priceCode = priceCode;
}
public int get_priceCode() {
return _priceCode;
}
public void set_priceCode(int code) {
_priceCode = code;
}
public String get_title() {
return _title;
}
}
3、Rental.java
package Movie_Ref;
/**
* 租赁
*
*/
public class Rental {
private Movie _movie; //影片
private int _daysRented; //租期
public Rental(Movie movie, int daysRented) {
_movie = movie;
_daysRented = daysRented;
}
public int get_daysRented() {
return _daysRented;
}
public Movie get_movie() {
return _movie;
}
double getCharge() { //--计算一笔租片费用
double result = 0;
switch(get_movie().get_priceCode()){ //--取得影片出租价格
case Movie.REGULAR: //--普通片
result += 2;
if (get_daysRented() > 2)
result += (get_daysRented() - 2) * 1.5;
break;
case Movie.NEW_RELEASE: //--新片
result += get_daysRented() * 3;
break;
case Movie.CHILDRENS: //--儿童片
result += 1.5;
if (get_daysRented() > 3)
result += (get_daysRented() - 3) * 1.5;
break;
}
return result;
}
int getFrequentRenterPoints() {
if (get_movie().get_priceCode() == Movie.NEW_RELEASE &&
get_daysRented() > 1)
return 2;
else
return 1;
}
}