《重构-改善既有代码的设计》-第1例:租赁影片(1)
2017-04-22 10:53
656 查看
买了《重构 - 改善既有代码的设计 》一书,一直没有好好看,大致过了下也觉得只是有点点印象而已,最后还是决定把代码敲一次,记录一下这些学习过程。
第一例:租赁影片
程序说明:顾客租了哪些影片,租期多长,根据租赁时间和影片类型算出费用和积分。
1. 分解并重组statement()
原代码如下有3个类。
1.1 抽离switch 语句到独立方法
1.1.1 Customer类改为:
1.1.2 注意 amountFor 方法的返回类型应该是double类型:
Customer类改为:
1.3 变量名称应该见名知意:好的代码应该清楚的表达出自己的功能,变量名称是代码清晰的一个关键。
amountFor方法中局部变量 thisAmount , 参数each 要改名。
改变量名 , Customer类改为:
1.4 函数应该放在它所使用的数据所属的对象中, 顾客租金的计算应该移动到Rental类中去。
Rental 类改为 : (计算租金方法去掉参数并改名 )
原顾客类中amountFor方法直接调用计算租金方法即可,Customer 类改为:
1.5 去掉旧函数amountFor,直接调用新函数getCharge。Customer类改为 :
1.6 尽量去掉临时变量,临时变量会导致大量参数的传递,没有必要。 thisAmount 是个多余的临时变量,直接去掉。
Customer 改为:
1.7 把积分计算方法放到Rental类中,写为常客积分计算方法(getFrequentRenterPoints),并改变Customer类中积分计算代码 。
Rental类改为:
Customer类中statement方法改为:
1.8 去掉statement方法中的2个临时变量:totalAmount 和 frequentRenterPoints 。抽离出对应计算方法,并调用。
Customer 类改为:
本步改写作用说明:
这里虽然从1个循环变为3个,但是多了2个查询函数。
1) 使得Customer 类中的任何代码都可以调用这些查询函数。
2) 若系统其它部分需要这些信息,也可以轻松地将查询函数加入 Customer 类接口。而若没有这些查询波函数,其它函数就必须了解 Rental 类,并自行建立循环。
1.9 加功能:打印凭条。
statement 方法改为 htmlStatement :
未完,见 : 重构-改善既有代码的设计-第1例:租赁影片(2)
第一例:租赁影片
程序说明:顾客租了哪些影片,租期多长,根据租赁时间和影片类型算出费用和积分。
1. 分解并重组statement()
原代码如下有3个类。
package bean; /** * 租赁订单 * @author Administrator */ 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; } }
package bean; /** * 影片 * @author Administrator */ 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 Movie(String _title, int _priceCode) { this._title = _title; this._priceCode = _priceCode; } public int getPriceCode() { return _priceCode; } public void setPriceCode(int _priceCode) { this._priceCode = _priceCode; } public String getTitle() { return _title; } }
package bean; import java.util.Enumeration; import java.util.Vector; /** * 顾客 * @author Administrator */ public class Customer { private String _name; // 顾客名字 private Vector _rentals = new Vector(); // 租赁订单数组 public Customer(String name) { super(); this._name = name; } public void addRental(Rental arg){ _rentals.addElement(arg); } public String getName() { return _name; } /** * 生成订单 * @return */ public String statement(){ double totalAmount = 0; // 总租金 int frequentRenterPoints = 0; // 积分 Enumeration 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.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; } // 增加积分 frequentRenterPoints ++; // 新片+租赁时间达2天 积分+1 if(each.getMovie().getPriceCode() == Movie.NEW_RELEASE && each.getDaysRented() > 1){ frequentRenterPoints ++; } // 本次租赁记录说明 result += "\t"+each.getMovie().getTitle()+"\t"+ String.valueOf(thisAmount)+"\n"; totalAmount += thislAmount; } // 页脚 result +="Amount owed is "+ String.valueOf(totalAmount)+"\n"; result +="You eared "+String.valueOf(frequentRenterPoints)+"frequent renter points"; return result; } }
1.1 抽离switch 语句到独立方法
1.1.1 Customer类改为:
package bean; import java.util.Enumeration; import java.util.Vector; /** * 顾客 * @author Administrator */ public class Customer { private String _name; // 顾客名字 private Vector _rentals = new Vector(); // 租赁订单数组 public Customer(String name) { super(); this._name = name; } public void addRental(Rental arg){ _rentals.addElement(arg); } public String getName() { return _name; } /** * 生成订单 * @return */ public String statement(){ double totalAmount = 0; // 总租金 int frequentRenterPoints = 0; // 积分 Enumeration rentals = _rentals.elements(); String result = "Rental Record for "+ getName() + "\n"; while( rentals.hasMoreElements()){ double thisAmount = 0; // 租金 Rental each = (Rental)rentals.nextElement(); thisAmount = amountFor(each); // 计算租金 // 增加积分 frequentRenterPoints ++; // 新片+租赁时间达2天 积分+1 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 eared "+String.valueOf(frequentRenterPoints)+"frequent renter points"; return result; } // 计算租金 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; } }
1.1.2 注意 amountFor 方法的返回类型应该是double类型:
Customer类改为:
package bean; import java.util.Enumeration; import java.util.Vector; /** * 顾客 * @author Administrator */ public class Customer { private String _name; // 顾客名字 private Vector _rentals = new Vector(); // 租赁订单数组 public Customer(String name) { super(); this._name = name; } public void addRental(Rental arg){ _rentals.addElement(arg); } public String getName() { return _name; } /** * 生成订单 * @return */ public String statement(){ double totalAmount = 0; // 总租金 int frequentRenterPoints = 0; // 积分 Enumeration rentals = _rentals.elements(); String result = "Rental Record for "+ getName() + "\n"; while( rentals.hasMoreElements()){ double thisAmount = 0; // 租金 Rental each = (Rental)rentals.nextElement(); thisAmount = amountFor(each); // 计算租金 // 增加积分 frequentRenterPoints ++; // 新片+租赁时间达2天 积分+1 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 eared "+String.valueOf(frequentRenterPoints)+"frequent renter points"; return result; } // 计算租金 private double amountFor(Rental each){ double 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; } }
1.3 变量名称应该见名知意:好的代码应该清楚的表达出自己的功能,变量名称是代码清晰的一个关键。
amountFor方法中局部变量 thisAmount , 参数each 要改名。
改变量名 , Customer类改为:
package bean; import java.util.Enumeration; import java.util.Vector; /** * 顾客 * @author Administrator */ public class Customer { private String _name; // 顾客名字 private Vector _rentals = new Vector(); // 租赁订单数组 public Customer(String name) { super(); this._name = name; } public void addRental(Rental arg){ _rentals.addElement(arg); } public String getName() { return _name; } /** * 生成订单 * @return */ public String statement(){ double totalAmount = 0; // 总租金 int frequentRenterPoints = 0; // 积分 Enumeration rentals = _rentals.elements(); String result = "Rental Record for "+ getName() + "\n"; while( rentals.hasMoreElements()){ double thisAmount = 0; // 租金 Rental each = (Rental)rentals.nextElement(); thisAmount = amountFor(each); // 计算租金 // 增加积分 frequentRenterPoints ++; // 新片+租赁时间达2天 积分+1 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 eared "+String.valueOf(frequentRenterPoints)+"frequent renter points"; return result; } // 计算租金 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; } }
1.4 函数应该放在它所使用的数据所属的对象中, 顾客租金的计算应该移动到Rental类中去。
Rental 类改为 : (计算租金方法去掉参数并改名 )
package bean; /** * 租赁订单 * @author Administrator */ 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; } }
原顾客类中amountFor方法直接调用计算租金方法即可,Customer 类改为:
package bean; import java.util.Enumeration; import java.util.Vector; /** * 顾客 * @author Administrator */ public class Customer { private String _name; // 顾客名字 private Vector _rentals = new Vector(); // 租赁订单数组 public Customer(String name) { super(); this._name = name; } public void addRental(Rental arg){ _rentals.addElement(arg); } public String getName() { return _name; } /** * 生成订单 * @return */ public String statement(){ double totalAmount = 0; // 总租金 int frequentRenterPoints = 0; // 积分 Enumeration rentals = _rentals.elements(); String result = "Rental Record for "+ getName() + "\n"; while( rentals.hasMoreElements()){ double thisAmount = 0; // 租金 Rental each = (Rental)rentals.nextElement(); thisAmount = amountFor(each); // 计算租金 // 增加积分 frequentRenterPoints ++; // 新片+租赁时间达2天 积分+1 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 eared "+String.valueOf(frequentRenterPoints)+"frequent renter points"; return result; } // 计算租金 private double amountFor(Rental aRental){ return aRental.getCharge(); } }
1.5 去掉旧函数amountFor,直接调用新函数getCharge。Customer类改为 :
package bean; import java.util.Enumeration; import java.util.Vector; /** * 顾客 * @author Administrator */ public class Customer { private String _name; // 顾客名字 private Vector _rentals = new Vector(); // 租赁订单数组 public Customer(String name) { super(); this._name = name; } public void addRental(Rental arg){ _rentals.addElement(arg); } public String getName() { return _name; } /** * 生成订单 * @return */ public String statement(){ double totalAmount = 0; // 总租金 int frequentRenterPoints = 0; // 积分 Enumeration rentals = _rentals.elements(); String result = "Rental Record for "+ getName() + "\n"; while( rentals.hasMoreElements()){ double thisAmount = 0; // 租金 Rental each = (Rental)rentals.nextElement(); thisAmount = each.getCharge(); // 计算租金 // 增加积分 frequentRenterPoints ++; // 新片+租赁时间达2天 积分+1 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 eared "+String.valueOf(frequentRenterPoints)+"frequent renter points"; return result; } }
1.6 尽量去掉临时变量,临时变量会导致大量参数的传递,没有必要。 thisAmount 是个多余的临时变量,直接去掉。
Customer 改为:
package bean; import java.util.Enumeration; import java.util.Vector; /** * 顾客 * @author Administrator */ public class Customer { private String _name; // 顾客名字 private Vector _rentals = new Vector(); // 租赁订单数组 public Customer(String name) { super(); this._name = name; } public void addRental(Rental arg){ _rentals.addElement(arg); } public String getName() { return _name; } /** * 生成订单 * @return */ public String statement(){ double totalAmount = 0; // 总租金 int frequentRenterPoints = 0; // 积分 Enumeration rentals = _rentals.elements(); String result = "Rental Record for "+ getName() + "\n"; while( rentals.hasMoreElements()){ Rental each = (Rental)rentals.nextElement(); // 增加积分 frequentRenterPoints ++; // 新片+租赁时间达2天 积分+1 if(each.getMovie().getPriceCode() == Movie.NEW_RELEASE && each.getDaysRented() > 1){ frequentRenterPoints ++; } // 本次租赁记录说明 result += "\t"+each.getMovie().getTitle()+"\t"+ String.valueOf(each.getCharge())+"\n"; totalAmount += each.getCharge(); } // 页脚 result +="Amount owed is "+ String.valueOf(totalAmount)+"\n"; result +="You eared "+String.valueOf(frequentRenterPoints)+"frequent renter points"; return result; } }
1.7 把积分计算方法放到Rental类中,写为常客积分计算方法(getFrequentRenterPoints),并改变Customer类中积分计算代码 。
Rental类改为:
package bean; /** * 租赁订单 * @author Administrator */ 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; } // 常客积分计算 int getFrequentRenterPoints(){ // 增加积分 int frequentRenterPoints =0; frequentRenterPoints++; // (新片+租赁时间达2天 积分+1 ) if(getMovie().getPriceCode() == Movie.NEW_RELEASE && getDaysRented() > 1){ frequentRenterPoints ++; } return frequentRenterPoints; } }
Customer类中statement方法改为:
/** * 生成订单 * @return */ public String statement(){ double totalAmount = 0; // 总租金 int frequentRenterPoints = 0; // 积分 Enumeration rentals = _rentals.elements(); String result = "Rental Record for "+ getName() + "\n"; while( rentals.hasMoreElements()){ Rental each = (Rental)rentals.nextElement(); // 积分 frequentRenterPoints += each.getFrequentRenterPoints(); // 本次租赁记录说明 result += "\t"+each.getMovie().getTitle()+"\t"+ String.valueOf(each.getCharge())+"\n"; totalAmount += each.getCharge(); } // 页脚 result +="Amount owed is "+ String.valueOf(totalAmount)+"\n"; result +="You eared "+String.valueOf(frequentRenterPoints)+"frequent renter points"; return result; }
1.8 去掉statement方法中的2个临时变量:totalAmount 和 frequentRenterPoints 。抽离出对应计算方法,并调用。
Customer 类改为:
package bean; import java.util.Enumeration; import java.util.Vector; /** * 顾客 * @author Administrator */ public class Customer { private String _name; // 顾客名字 private Vector _rentals = new Vector(); // 租赁订单数组 public Customer(String name) { super(); this._name = name; } public void addRental(Rental arg){ _rentals.addElement(arg); } public String getName() { return _name; } /** * 生成订单 * @return */ public String statement(){ 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.getCharge())+"\n"; } // 页脚 result +="Amount owed is "+ String.valueOf(getTotalCharge())+"\n"; result +="You eared "+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; } }
本步改写作用说明:
这里虽然从1个循环变为3个,但是多了2个查询函数。
1) 使得Customer 类中的任何代码都可以调用这些查询函数。
2) 若系统其它部分需要这些信息,也可以轻松地将查询函数加入 Customer 类接口。而若没有这些查询波函数,其它函数就必须了解 Rental 类,并自行建立循环。
1.9 加功能:打印凭条。
statement 方法改为 htmlStatement :
/** * 生成订单(打印凭条) * @return */ public String htmlStatement(){ Enumeration rentals = _rentals.elements(); String result = "<P><H1>Rentals for <EM> "+ getName() + "</EM></H1></P>\n"; while( rentals.hasMoreElements()){ Rental each = (Rental)rentals.nextElement(); // 本次租赁记录说明 result += each.getMovie().getTitle()+":"+ String.valueOf(each.getCharge())+"<BR>\n"; } // 页脚 result +="<P>You owe <EM>"+ String.valueOf(getTotalCharge())+"</EM></P>\n"; result +="<P> on this rental you earned <EM> "+String.valueOf(getTotalFrequentRenterPoints())+"</EM> frequent renter points </P>"; return result; }
未完,见 : 重构-改善既有代码的设计-第1例:租赁影片(2)
相关文章推荐
- 《重构-改善既有代码的设计》-第1例:租赁影片(2)
- 《重构-改善既有代码的设计》读书笔记(一)
- 重构:改善既有代码的设计(软件开发的不朽经典)
- 重构-改善既有代码的设计总结
- 重构-改善既有代码的设计Refactoring - Improving the Design of Existing Code
- 重构-改善既有代码的设计
- 《重构--改善既有代码的设计 》
- 笔记-重构-改善既有代码的设计(Refactoring - Improving the Design of Existing Code)
- 学习《重构-改善既有代码的设计》一
- 《重构-改善既有代码的设计》清单
- 重构-改善既有代码的设计
- 《重构-改善既有代码的设计》笔记1
- 读重构-改善既有代码的设计笔记
- 《重构-改善既有代码的设计》 读书心得
- 《重构-改善既有的代码设计》读书笔记
- 《重构-改善代码既有的设计》英文目录
- 谁有《重构-改善既有代码的设计》电子版
- 《重构—改善既有代码设计》——第二章重构原则——学习笔记
- 重构-改善既有代码的设计——第一章 重构,第一个案例
- 《重构——改善现有代码的设计》 读书笔记