前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >《重构-改善既有代码的设计》-第1例:租赁影片(1)

《重构-改善既有代码的设计》-第1例:租赁影片(1)

作者头像
微风-- 轻许--
发布2022-04-13 09:30:55
2500
发布2022-04-13 09:30:55
举报
文章被收录于专栏:java 微风java 微风

买了《重构 - 改善既有代码的设计 》一书,一直没有好好看,大致过了下也觉得只是有点点印象而已,最后还是决定把代码敲一次,记录一下这些学习过程。

第一例:租赁影片

程序说明:顾客租了哪些影片,租期多长,根据租赁时间和影片类型算出费用和积分。

1. 分解并重组statement()

原代码如下有3个类。

代码语言:javascript
复制
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;
	}
	
	
}
代码语言:javascript
复制
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;
	}
	
}
代码语言:javascript
复制
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类改为:

代码语言:javascript
复制
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类改为:

代码语言:javascript
复制
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类改为:

代码语言:javascript
复制
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 类改为 : (计算租金方法去掉参数并改名 )

代码语言:javascript
复制
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 类改为:

代码语言:javascript
复制
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类改为 :

代码语言:javascript
复制
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 改为:

代码语言:javascript
复制
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类改为:

代码语言:javascript
复制
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方法改为:

代码语言:javascript
复制
/**
	 * 生成订单
	 * @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 类改为:

代码语言:javascript
复制
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 :

代码语言:javascript
复制
/**
	 * 生成订单(打印凭条)
	 * @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)

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017-04-22 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档