示例:影片出租店程序(重构——分解并重组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; } }