《重构》 — Java示例:影片出租店程序(6、重构——引入状态模式)

来源:互联网 发布:外汇自动交易软件 编辑:程序博客网 时间:2024/06/06 20:54

示例:影片出租店程序(重构——引入状态模式)
由于考虑到“影片分类结构”、“费用计算规则”或“常客积点计算规则”在未来可能会发生改变,所以通过引入“状态模式”将“费用计算”和“常客积点计算”中“因条件而异的代码”替换掉。

步骤:
1、移动“金额计算”方法 —— “搬移方法(Move Method)”
由于Rental类的GetCharge方法中使用到了Movie类的属性,所以这暗示应将GetCharge方法移动到Movie类中。

Eclipse工具重构步骤:
(1)、选中“方法名”,右键选择“Move”

clip_image002
(2)、确认移动位置

clip_image002[5]
(3)、调整代码如下
public class Movie {
    …………

    double getCharge(Rental rental) {    //--计算一笔租片费用
        double result = 0;
        switch(rental.get_movie().get_priceCode()){  //--取得影片出租价格
            case Movie.REGULAR: //--普通片
                result += 2;
                if (rental.get_daysRented() > 2)
                    result += (rental.get_daysRented() - 2) * 1.5;
                break;
            case Movie.NEW_RELEASE:  //--新片
                result += rental.get_daysRented() * 3;
                break;
            case Movie.CHILDRENS: //--儿童片
                result += 1.5;
                if (rental.get_daysRented() > 3)
                    result += (rental.get_daysRented() - 3) * 1.5;
                break;
        }
        return result;
    }
}

public class Rental {
    …………
    double getCharge() {
        return _movie.getCharge(this);
    }
}

(4)、选中代码,右键选择“Extract Local Variable”

clip_image002[7]
(5)、输入变量名

clip_image002[9]
(4)、调整代码如下
public class Movie {
    …………

    double getCharge(int daysRented) {    //--计算一笔租片费用
        double result = 0;

        switch(get_priceCode()){  //--取得影片出租价格
            case Movie.REGULAR: //--普通片
                result += 2;
                if (daysRented > 2)
                    result += (daysRented - 2) * 1.5;
                break;
            case Movie.NEW_RELEASE:  //--新片
                result += daysRented * 3;
                break;
            case Movie.CHILDRENS: //--儿童片
                result += 1.5;
                if (daysRented > 3)
                    result += (daysRented - 3) * 1.5;
                break;
        }
        return result;
    }
}

public class Rental {
    …………
    double getCharge() {
        return _movie.getCharge(get_daysRented());
    }
}
2、移动“常客积点计算”方法 —— “搬移方法(Move Method)”
public class Movie {
    …………
    int getFrequentRenterPoints(int daysRented) {
        if (get_priceCode() == Movie.NEW_RELEASE && daysRented > 1)
            return 2;
        else
            return 1;
    }
}
public class Rental {
    …………
    int getFrequentRenterPoints() {
        return _movie.getFrequentRenterPoints(get_daysRented());
    }
}

3、重构“费用计算”方法 —— 引入状态模式
运用“状态模式”取代与价格相关的条件逻辑。

3.1、替换“价格类型码”——“替换类型码(Replace Type Code with State/Strategy)”
运用“替换类型码” 将“与型别相依的行为”搬移至“状态类”内。

1、封装“价格字段”——“封装字段(Self Encapsulate Field)”
第一步是针对“与型别相依的行为”使用“封装字段” ,确保任何时候都通过getting和setting两个函数来运用这些行为。

public class Movie {
    …………
    public Movie(String title, int priceCode) {
        _title = title;
        set_priceCode(priceCode);
    }
}

2、创建“状态类”
第二步是实现price类,并在其中提供“与型别相依的行为”。

Eclipse工具重构步骤:
(1)、选中类,右键选择“Override/Implement Methods”

clip_image002[11]
(2)、选择方法

clip_image002[13]
(3)、调整代码如下
abstract class Price {
    abstract int get_priceCode(); //--取得价格代号
}
class ChildrensPrice extends Price{

    int get_priceCode() {
        return Movie.CHILDRENS;
    }
}
class NewReleasePrice extends Price{

    int get_priceCode() {
        return Movie.NEW_RELEASE;
    }
}
class RegularPrice extends Price{

    int get_priceCode() {
        return Movie.REGULAR;
    }
}

3、修改“价格代号”访问函数
第三步,修改Movie类内的“价格代号”访问函数, 让它们使用Price类。

public class Movie {
    private Price _price; //价格
    public int get_priceCode() { //--取得价格代码
        return _price.get_priceCode();
    }

    public void set_priceCode(int arg) {
        switch(arg){
            case REGULAR:    //--普通片
                _price = new RegularPrice();
                break;
            case CHILDRENS:    //--儿童片
                _price = new ChildrensPrice();
                break;
            case NEW_RELEASE:    //--新片
                _price = new NewReleasePrice();
                break;   
            default:
                throw new IllegalArgumentException("Incorrect PriceCode");
        }       
    }
}

3.2、搬移“费用计算”方法 ——“搬移方法(Move Method)”
对Movie.GetCharge运用“搬移方法”将switch语句移到Price类里头。

abstract class Price {
    abstract int get_priceCode(); //--取得价格代号

    double getCharge(int daysRented) {    //--计算一笔租片费用
        double result = 0;
        switch(get_priceCode()){  //--取得影片出租价格
            case Movie.REGULAR: //--普通片
                result += 2;
                if (daysRented > 2)
                    result += (daysRented - 2) * 1.5;
                break;
            case Movie.NEW_RELEASE:  //--新片
                result += daysRented * 3;
                break;
            case Movie.CHILDRENS: //--儿童片
                result += 1.5;
                if (daysRented > 3)
                    result += (daysRented - 3) * 1.5;
                break;
        }
        return result;
    }
}

public class Movie {
    double getCharge(int daysRented) {
        return _price.getCharge(daysRented);
    }
}

3.3、替换switch语句——“用多态替换条件语句(Replace Condional with Polymorphism)”
最后运用“用多态替换条件语句”去掉Price.GetCharge方法中的switch语句。

1、提取Movie.REGULAR分支
作法是每次取出一个case分支,在相应的 class 内建立一个“覆写方法(overriding method)”。
先从RegularPrice类开始,RegularPrice.GetCharge方法覆写了父类中的case语句。

class RegularPrice extends Price{

    int get_priceCode() {
        return Movie.REGULAR;
    }

    double getCharge(int daysRented) {
        double result = 2;       
        if (daysRented > 2)
            result += (daysRented - 2) * 1.5;
        return result;
    }   
}

2、提取Movie.NEW_RELEASE分支
class NewReleasePrice extends Price{

    int get_priceCode() {
        return Movie.NEW_RELEASE;
    }
    double getCharge(int daysRented) {
        return daysRented * 3;
    }   
}

3、提取Movie.CHILDRENS分支
class ChildrensPrice extends Price{

    int get_priceCode() {
        return Movie.CHILDRENS;
    }
    double getCharge(int daysRented) {   
        double result = 1.5;
        if (daysRented > 3)
            result += (daysRented - 3) * 1.5;

        return result;
    }   
}

4、处理Price.GetCharge
处理完所有case分支之后,把Price.GetCharge声明为abstract。
abstract class Price {
    abstract int get_priceCode(); //--取得价格代号
    abstract double getCharge(int daysRented);    //--计算一笔租片费用
}

4、重构“常客积点计算”方法 —— 引入状态模式
运用“状态模式”取代Movie.GetFrequentRenterPoints方法中“与型别相依的行为”,也就是“判断是否为新片self.PriceCode = NEW_RELEASE”那个动作。

4.1、搬移“常客积点计算”方法 ——“搬移方法(Move Method)”
abstract class Price {
    int getFrequentRenterPoints(int daysRented) {
        if (get_priceCode() == Movie.NEW_RELEASE && daysRented > 1)
            return 2;
        else
            return 1;
    }
}

public class Movie {
    int getFrequentRenterPoints(int daysRented) {
        return _price.getFrequentRenterPoints(daysRented);
    }   
}

4.2、替换条件语句——“用多态替换条件语句(Replace Condional with Polymorphism)”
不需声明TPrice.GetFrequentRenterPoints函数为abstract,使其成为一种缺省行为。只为“新片类型”产生一个覆写函数。
abstract class Price {
    int getFrequentRenterPoints(int daysRented) {
        return 1;
    }
}
class NewReleasePrice extends Price{
    int getFrequentRenterPoints(int daysRented) {
        return daysRented > 1 ? 2 : 1;
    }   
}

代码:
1、Customer.java
package movie_Ref1;

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

    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;
    }
    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;
    }
    public String htmlStatement() {   
        Enumeration rentals = _rentals.elements();
        String result = "

Rentals for " + get_name() + "

/n";
        while(rentals.hasMoreElements()){
            Rental each = (Rental) rentals.nextElement(); //--取得一笔租借记录                       
            //---显示此笔租借数据
            result += each.get_movie().get_title() + ": " +
                      String.valueOf(each.getCharge()) + "
/n";
        }
        //---结尾打印
        result += "

You owe " + String.valueOf(getTotalCharge()) + "

/n";
        result += "On this rental you earned " + String.valueOf(getTotalFrequentRenterPoints()) +
                 "
frequent renter points

";
        return result;       
    }
}

2、Movie.java
package movie_Ref1;

/**
* 影片
*
*/
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 Price _price; //价格
    public Movie(String title, int priceCode) {
        _title = title;
        set_priceCode(priceCode);
    }

    public String get_title() {
        return _title;
    }
    public int get_priceCode() { //--取得价格代码
        return _price.get_priceCode();
    }
    public void set_priceCode(int arg) {
        switch(arg){
            case REGULAR:
                _price = new RegularPrice();
                break;
            case CHILDRENS:
                _price = new ChildrensPrice();
                break;
            case NEW_RELEASE:
                _price = new NewReleasePrice();
                break;   
            default:
                throw new IllegalArgumentException("Incorrect PriceCode");
        }       
    }
    double getCharge(int daysRented) {
        return _price.getCharge(daysRented);
    }
    int getFrequentRenterPoints(int daysRented) {
        return _price.getFrequentRenterPoints(daysRented);
    }   
}

3、Rental.java
package movie_Ref1;

/**
* 租赁
*
*/
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() {
        return _movie.getCharge(get_daysRented());
    }

    int getFrequentRenterPoints() {
        return _movie.getFrequentRenterPoints(get_daysRented());
    }
}

4、Price.java
package movie_Ref1;

abstract class Price {
    abstract int get_priceCode(); //--取得价格代号
    abstract double getCharge(int daysRented);    //--计算一笔租片费用
    int getFrequentRenterPoints(int daysRented) {
        return 1;
    }
}
class ChildrensPrice extends Price{
    int get_priceCode() {
        return Movie.CHILDRENS;
    }
    double getCharge(int daysRented) {   
        double result = 1.5;
        if (daysRented > 3)
            result += (daysRented - 3) * 1.5;

        return result;
    }   
}
class NewReleasePrice extends Price{
    int get_priceCode() {
        return Movie.NEW_RELEASE;
    }
    double getCharge(int daysRented) {
        return daysRented * 3;
    }
    int getFrequentRenterPoints(int daysRented) {
        return daysRented > 1 ? 2 : 1;
    }   
}
class RegularPrice extends Price{
    int get_priceCode() {
        return Movie.REGULAR;
    }
    double getCharge(int daysRented) {
        double result = 2;       
        if (daysRented > 2)
            result += (daysRented - 2) * 1.5;
        return result;
    }   
}

原创粉丝点击