您的位置:首页 > 编程语言

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

2017-04-22 13:52 369 查看
上接  重构-改善既有代码的设计-第1例:租赁影片(1)

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

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

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

Movie 类改为:

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 类中的计算租金方法和常客积分计算方法 改为:

/**
* 常客积分计算
* @return
*/
int getFrequentRenterPoints(){
return _movie.getFrequentRenterPoints(_daysRented);
}

/**
* 计算租金
* @return
*/
double getCharge(){
return _movie.getCharge(_daysRented);
}


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

Movie 类的构造之前为:

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;
}

让构造不能直接访问 不愿意被外界直接访问的属性,构造现在改为:

public Movie(String _title, int _priceCode) {
this._title = _title;
setPriceCode(_priceCode);
}


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

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

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

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

 2.3.1 Price 及子类 :

package bean;

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

abstract int getPriceCode();
}

package bean;

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

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

}

package bean;

public class NewReleasePrice extends Price {

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

}

package bean;

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

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

}


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

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 类中调用即可。

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;
}
}

/**
* 计算租金
* @param dayRented 租赁天数
* @return
*/
double getCharge(int dayRented){
return _price.getCharge(dayRented); // 这是在 Movie 类中的调用
}


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

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

租金类 Price 重构前:

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类 及子类 为:

package bean;

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

abstract int getPriceCode();

/**
* 计算租金
* @param dayRented 租赁天数
* @return
*/
abstract double getCharge(int dayRented);

}

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;
}
}

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;
}

}

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的积分计算方法就行了。

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;
}
}
}

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);
}

}


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

package bean;

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

abstract int getPriceCode();

...

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


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

到此,重构-改善既有代码的设计-第1例:租赁影片,就重构完成了。

总结 :这样重构以后,不论是修改影片分类结构,还是修改租金计算规则又或积分计算规则就都容易多了 。

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

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

最后 所有类完整代码为:

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;
}

}


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);
}

}

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);
}

}

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;
}
}

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;
}
}

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 ;
}
}
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;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息