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

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

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

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

2 运用多态取代与价格相关的条件逻辑

2.1 最好不要在另一个对象的属性基础上运用switch语句,应该在对象自己的数据上使用。

2.1.1 移动 getCharge ,getFrequentRenterPoints 方法到Movie 类中去。把会根据影片类型的变化而变化的东西放在影片类中。

Movie 类改为:

代码语言: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;
	}
	
	/**
	 * 计算租金
	 * @param dayRented 租赁天数
	 * @return
	 */
	double getCharge(int dayRented){
		double result = 0; // 租金
		// 确定每种片子的租金
		switch(getPriceCode()){
			case Movie.REGULAR:
				result += 2;
				if(dayRented > 2 ){
					result += (dayRented - 2) * 1.5;
				}
				break;
			case Movie.NEW_RELEASE:
				result += dayRented*3;
				break;
			case Movie.CHILDRENS:
				result += 1.5;
				if(dayRented > 3 ){
					result += (dayRented - 3) * 1.5;
				}
				break;
		}
		
		return result;
	}
	
	/**
	 * 常客积分计算
	 * @param dayRented 租赁天数
	 * @return
	 */
	int getFrequentRenterPoints(int dayRented){
		// (新片+租赁时间达2天  积分+1 )
		if(getPriceCode() == Movie.NEW_RELEASE && dayRented > 1){
			return 2;
		}else{
			return 1;
		}
	}
	
}

2.1.2 修改Rental 类中的 getCharge ,getFrequentRenterPoints方法,让它调用Movie 类提供的新函数。

Rental 类中的计算租金方法和常客积分计算方法 改为:

代码语言:javascript
复制
	/**
	 * 常客积分计算
	 * @return
	 */
	int getFrequentRenterPoints(){
		return _movie.getFrequentRenterPoints(_daysRented);
	}
	
	/**
	 * 计算租金
	 * @return
	 */
	double getCharge(){
		return _movie.getCharge(_daysRented);
	}

2.2 为了确保任何时候都要通过取值函数和赋值函数来访问 不愿意被外界直接访问的属性,我们用一个对赋值函数的调用来代替构造中的部分代码。

Movie 类的构造之前为:

代码语言:javascript
复制
	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
复制
public Movie(String _title, int _priceCode) {
		this._title = _title;
		setPriceCode(_priceCode);
	}

2.3 有新需求到来,有新品种影片,租金计算又有新算法 。

用到设计模式的状态模式State(对象行为型)

于是新建一个Price抽象类,并在其内给2个抽象方法用于获取影片的计价类型和计算租金,积分计算。

再写多个子类继承Price并各自实现父类方法以实现对租金计算,积分计算的重构。

2.3.1 Price 及子类 :

代码语言:javascript
复制
package bean;

/**
 * 租金
 * @author Administrator
 */
public abstract class Price {

	abstract int getPriceCode();
}
代码语言:javascript
复制
package bean;

/**
 * 儿童片租金
 * @author Administrator
 */
public class ChildrensPrice extends Price {

	@Override
	int getPriceCode() {
		return Movie.CHILDRENS;
	}

}
代码语言:javascript
复制
package bean;

public class NewReleasePrice extends Price {

	@Override
	int getPriceCode() {
		return Movie.NEW_RELEASE;
	}

}
代码语言:javascript
复制
package bean;

/**
 * 普通片租金
 * @author Administrator
 */
public class RegularPrice extends Price {

	@Override
	int getPriceCode() {
		return Movie.REGULAR;
	}

}

2.3.2 修改Movie 类的计价类型属性为Price类型,并改写赋值函数 :

代码语言: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 Price _price; 
	
	public Movie(String _title, int _priceCode) {
		this._title = _title;
		setPriceCode(_priceCode);
	}

	public int getPriceCode() {
		return _price.getPriceCode();
	}

	public void setPriceCode(int _priceCode) {
		switch(_priceCode){
			case REGULAR:
				_price = new RegularPrice();
				break;
			case NEW_RELEASE:
				_price = new NewReleasePrice();
				break;
			case CHILDRENS:
				_price = new ChildrensPrice();
				break;
			default:
				throw new IllegalArgumentException(" Incorrect PriceCode! ");
		}
	}    ...

2.3.4 把租金计算方法移动到 Price 类,在Movie 类中调用即可。

代码语言:javascript
复制
package bean;

/**
 * 租金
 * @author Administrator
 */
public abstract class Price {

	abstract int getPriceCode();
	
	/**
	 * 计算租金
	 * @param dayRented 租赁天数
	 * @return
	 */
	double getCharge(int dayRented){
		double result = 0; // 租金
		// 确定每种片子的租金
		switch(getPriceCode()){
			case Movie.REGULAR:
				result += 2;
				if(dayRented > 2 ){
					result += (dayRented - 2) * 1.5;
				}
				break;
			case Movie.NEW_RELEASE:
				result += dayRented*3;
				break;
			case Movie.CHILDRENS:
				result += 1.5;
				if(dayRented > 3 ){
					result += (dayRented - 3) * 1.5;
				}
				break;
		}
		
		return result;
	}
}
代码语言:javascript
复制
	/**
	 * 计算租金
	 * @param dayRented 租赁天数
	 * @return
	 */
	double getCharge(int dayRented){
		return _price.getCharge(dayRented); // 这是在 Movie 类中的调用
	}

2.3.5 重构租金计算方法,把每个getCharge 方法中switch 的每个 case 取出,在相应的Price子类中写一个覆盖函数。

最后把Price的 租金计算方法改为抽象方法。

租金类 Price 重构前:

代码语言:javascript
复制
package bean;

/**
 * 租金
 * @author Administrator
 */
public abstract class Price {

	abstract int getPriceCode();
	
	/**
	 * 计算租金
	 * @param dayRented 租赁天数
	 * @return
	 */
	double getCharge(int dayRented){
		double result = 0; // 租金
		// 确定每种片子的租金
		switch(getPriceCode()){
			case Movie.REGULAR:
				result += 2;
				if(dayRented > 2 ){
					result += (dayRented - 2) * 1.5;
				}
				break;
			case Movie.NEW_RELEASE:
				result += dayRented*3;
				break;
			case Movie.CHILDRENS:
				result += 1.5;
				if(dayRented > 3 ){
					result += (dayRented - 3) * 1.5;
				}
				break;
		}
		
		return result;
	}
}

重构getCharge 方法后Price类 及子类 为:

代码语言:javascript
复制
package bean;

/**
 * 租金
 * @author Administrator
 */
public abstract class Price {

	abstract int getPriceCode();
	
	/**
	 * 计算租金
	 * @param dayRented 租赁天数
	 * @return
	 */
	abstract double getCharge(int dayRented);
		
}
代码语言:javascript
复制
package bean;

/**
 * 儿童片租金
 * @author Administrator
 */
public class ChildrensPrice extends Price {

	@Override
	int getPriceCode() {
		return Movie.CHILDRENS;
	}

	/**
	 * 计算租金
	 * @param dayRented 租赁天数
	 * @return
	 */
	@Override
	double getCharge(int dayRented){
		double result = 1.5;
		if(dayRented > 3){
			result += (dayRented - 3) * 1.5;
		}
		return result;
	}
}
代码语言:javascript
复制
package bean;
/**
 * 新片租金
 * @author Administrator
 */
public class NewReleasePrice extends Price {

	@Override
	int getPriceCode() {
		return Movie.NEW_RELEASE;
	}
	
	/**
	 * 计算租金
	 * @param dayRented 租赁天数
	 * @return
	 */
	@Override
	double getCharge(int dayRented){
		return dayRented*3;
	}

}
代码语言:javascript
复制
package bean;

/**
 * 普通片租金
 * @author Administrator
 */
public class RegularPrice extends Price {

	@Override
	int getPriceCode() {
		return Movie.REGULAR;
	}

	/**
	 * 计算租金
	 * @param dayRented 租赁天数
	 * @return
	 */
	@Override
	double getCharge(int dayRented){
		double result = 2;
		if(dayRented > 2){
			result += (dayRented - 2) * 1.5;
		}
		return result;
	}
}

2.3.6 对积分计算方法作相同重构。

从 Movie 类中移动积分计算方法到 Price 类中。Movie 类中调用Proce的积分计算方法就行了。

代码语言:javascript
复制
package bean;

/**
 * 租金
 * @author Administrator
 */
public abstract class Price {

	abstract int getPriceCode();
	
	/**
	 * 计算租金
	 * @param dayRented 租赁天数
	 * @return
	 */
	abstract double getCharge(int dayRented);
		
	/**
	 * 常客积分计算
	 * @param dayRented 租赁天数
	 * @return
	 */
	int getFrequentRenterPoints(int dayRented){
		// (新片+租赁时间达2天  积分+1 )
		if(getPriceCode() == Movie.NEW_RELEASE && dayRented > 1){
			return 2;
		}else{
			return 1;
		}
	}
}
代码语言: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 Price _price; 
	
	public Movie(String _title, int _priceCode) {
		this._title = _title;
		setPriceCode(_priceCode);
	}

	public int getPriceCode() {
		return _price.getPriceCode();
	}

	...
	
	/**
	 * 常客积分计算
	 * @param dayRented 租赁天数
	 * @return
	 */
	int getFrequentRenterPoints(int dayRented){
		return _price.getFrequentRenterPoints(dayRented);
	}
	
}
	...
	
	/**
	 * 常客积分计算
	 * @param dayRented 租赁天数
	 * @return
	 */
	int getFrequentRenterPoints(int dayRented){
		return _price.getFrequentRenterPoints(dayRented);
	}
	
}

对 Proce 类的积分计算方法重构,只是为新片类型增加一个覆写函数,并在超类中保留原函数,使它成为一种默认行为。

代码语言:javascript
复制
package bean;

/**
 * 租金
 * @author Administrator
 */
public abstract class Price {

	abstract int getPriceCode();
	
	...
		
	/**
	 * 常客积分计算
	 * @param dayRented 租赁天数
	 * @return
	 */
	int getFrequentRenterPoints(int dayRented){
		// 默认积1分
		return 1;
	}
}
代码语言:javascript
复制
package bean;
/**
 * 新片租金
 * @author Administrator
 */
public class NewReleasePrice extends Price {
	...
	/**
	 * 常客积分计算
	 * @param dayRented 租赁天数
	 * @return
	 */
	@Override
	int getFrequentRenterPoints(int dayRented){
		// (新片+租赁时间达2天  积分+1 )
		return (dayRented > 1) ? 2 : 1 ;
	}
}
	...
	/**
	 * 常客积分计算
	 * @param dayRented 租赁天数
	 * @return
	 */
	@Override
	int getFrequentRenterPoints(int dayRented){
		// (新片+租赁时间达2天  积分+1 )
		return (dayRented > 1) ? 2 : 1 ;
	}
}

到此,重构-改善既有代码的设计-第1例:租赁影片,就重构完成了。 总结 :这样重构以后,不论是修改影片分类结构,还是修改租金计算规则又或积分计算规则就都容易多了 。

注:个人觉得 Movie 类中的 setPriceCode 方法 中得每种 price 的时候不该用构造函数,而是该直接调用各Price 子类 中的 getPriceCode 方法。

但此博文尊重原书中代码未作改动。

最后 所有类完整代码为:

代码语言: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 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;
	}
	
	// 计算总积分
	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;
	}
	
}
代码语言: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;
	}
	
	/**
	 * 常客积分计算
	 * @return
	 */
	int getFrequentRenterPoints(){
		return _movie.getFrequentRenterPoints(_daysRented);
	}
	
	/**
	 * 计算租金
	 * @return
	 */
	double getCharge(){
		return _movie.getCharge(_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 Price _price; 
	
	public Movie(String _title, int _priceCode) {
		this._title = _title;
		setPriceCode(_priceCode);
	}

	public int getPriceCode() {
		return _price.getPriceCode();
	}

	public void setPriceCode(int _priceCode) {
		switch(_priceCode){
			case REGULAR:
				_price = new RegularPrice();
				break;
			case NEW_RELEASE:
				_price = new NewReleasePrice();
				break;
			case CHILDRENS:
				_price = new ChildrensPrice();
				break;
			default:
				throw new IllegalArgumentException(" Incorrect PriceCode! ");
		}
	}

	public String getTitle() {
		return _title;
	}
	
	/**
	 * 计算租金
	 * @param dayRented 租赁天数
	 * @return
	 */
	double getCharge(int dayRented){
		return _price.getCharge(dayRented);
	}
	
	/**
	 * 常客积分计算
	 * @param dayRented 租赁天数
	 * @return
	 */
	int getFrequentRenterPoints(int dayRented){
		return _price.getFrequentRenterPoints(dayRented);
	}
	
}
代码语言:javascript
复制
package bean;

/**
 * 租金+积分
 * @author Administrator
 */
public abstract class Price {

	abstract int getPriceCode();
	
	/**
	 * 计算租金
	 * @param dayRented 租赁天数
	 * @return
	 */
	abstract double getCharge(int dayRented);
		
	/**
	 * 常客积分计算
	 * @param dayRented 租赁天数
	 * @return
	 */
	int getFrequentRenterPoints(int dayRented){
		// 默认积1分
		return 1;
	}
}
代码语言:javascript
复制
package bean;

/**
 * 儿童片租金
 * @author Administrator
 */
public class ChildrensPrice extends Price {

	@Override
	int getPriceCode() {
		return Movie.CHILDRENS;
	}

	/**
	 * 计算租金
	 * @param dayRented 租赁天数
	 * @return
	 */
	@Override
	double getCharge(int dayRented){
		double result = 1.5;
		if(dayRented > 3){
			result += (dayRented - 3) * 1.5;
		}
		return result;
	}
}
代码语言:javascript
复制
package bean;
/**
 * 新片租金
 * @author Administrator
 */
public class NewReleasePrice extends Price {

	@Override
	int getPriceCode() {
		return Movie.NEW_RELEASE;
	}
	
	/**
	 * 计算租金
	 * @param dayRented 租赁天数
	 * @return
	 */
	@Override
	double getCharge(int dayRented){
		return dayRented*3;
	}

	/**
	 * 常客积分计算
	 * @param dayRented 租赁天数
	 * @return
	 */
	@Override
	int getFrequentRenterPoints(int dayRented){
		// (新片+租赁时间达2天  积分+1 )
		return (dayRented > 1) ? 2 : 1 ;
	}
}
代码语言:javascript
复制
package bean;

/**
 * 普通片租金
 * @author Administrator
 */
public class RegularPrice extends Price {

	@Override
	int getPriceCode() {
		return Movie.REGULAR;
	}

	/**
	 * 计算租金
	 * @param dayRented 租赁天数
	 * @return
	 */
	@Override
	double getCharge(int dayRented){
		double result = 2;
		if(dayRented > 2){
			result += (dayRented - 2) * 1.5;
		}
		return result;
	}
}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017-04-22 ,如有侵权请联系 cloudcommunity@tencent.com 删除

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

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

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

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