《重构-改善代码既有的设计》重构,第一个案例

起点:编写3个类的代码
1、第一个类-影片(Movie):

package com.lee.test.aFirstExample;

public class Movie {

    /**
     * @param title
     * @param priceCode
     */
    public Movie(String title, int priceCode) {
        super();
        this.title = title;
        this.priceCode = priceCode;
    }

    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 String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
    public int getPriceCode() {
        return priceCode;
    }
    public void setPriceCode(int priceCode) {
        this.priceCode = priceCode;
    }


}

2、第二个类-租赁(Rental):

package com.lee.test.aFirstExample;

public class Rental {
    /**
     * @param movie
     * @param dayRented
     */
    public Rental(Movie movie, int dayRented) {
        super();
        this.movie = movie;
        this.dayRented = dayRented;
    }

    private Movie movie;
    public Movie getMovie() {
        return movie;
    }

    private int dayRented;
    public int getDayRented() {
        return dayRented;
    }


}

3、第三个类-消费者(Customer):

package com.lee.test.aFirstExample;

import java.util.Enumeration;
import java.util.Vector;

public class Customer {
    /**
     * @param name
     */
    public Customer(String name) {
        super();
        this.name = name;
    }
    private String name;
    public String getName() {
        return name;
    }
    private Vector rentals = new Vector();

    public void addRental(Rental arg)
    {
        rentals.addElement(arg);
    }

    public String statement(){
        double totalAmount = 0;
        int frequentRenterPoints = 0;
        Enumeration rentalss = rentals.elements();
        String result = "Rental Record for" +" "+ getName()+"\n";
        while(rentalss.hasMoreElements())
        {
            double thisAmount = 0;
            Rental each = (Rental) rentalss.nextElement();

            switch(each.getMovie().getPriceCode())
            {
                case Movie.regular:
                thisAmount += 2;
                if(each.getDayRented()>2)
                    thisAmount += (each.getDayRented()-2)*1.5; 
                break;

                case Movie.new_release:
                thisAmount += each.getDayRented()*3;
                break;

                case Movie.childrens:
                    thisAmount += 1.5;
                    if(each.getDayRented()>3)
                        thisAmount += (each.getDayRented()-3)*1.5; 
                    break;
            }

            //积分  每借一张加1个积分
            frequentRenterPoints++;
            //积分累加条件  新版本的片子,借的时间大于1天
            if((each.getMovie().getPriceCode()==Movie.new_release)&&each.getDayRented()>1)
            {
                frequentRenterPoints++;
            }

            result +="\t" +each.getMovie().getTitle()+"\t" 
                    +String.valueOf(thisAmount)+"\n";

            totalAmount += thisAmount;
        }

        result += "Amount owed is "+ String.valueOf(totalAmount)+"\n";
        result +="You earned "+String.valueOf(frequentRenterPoints)+" "
                +"frequent renter points";
        return result;
    }


}

4、自己定义客户端(Client)。

package com.lee.test.aFirstExample;

public class Client {

    public static void main(String[] args) {

        Movie mov = new Movie("metal",2);
        Rental ren = new Rental(mov,8);
        Customer cus = new Customer("Lee");
        cus.addRental(ren);

        System.out.println(cus.statement());


    }


}

5、输出结果

Rental Record for Lee
    metal   9.0
Amount owed is 9.0
You earned 1 frequent renter points

6、重构第一步
提炼方法(Extract Method)
提炼Switch/Case分支到一个方法,修改后Customer代码:

package com.lee.test.aFirstExample;

import java.util.Enumeration;
import java.util.Vector;

public class Customer {
    /**
     * @param name
     */
    public Customer(String name) {
        super();
        this.name = name;
    }
    private String name;
    public String getName() {
        return name;
    }
    private Vector rentals = new Vector();

    public void addRental(Rental arg)
    {
        rentals.addElement(arg);
    }

    public String statement(){
        double totalAmount = 0;
        int frequentRenterPoints = 0;
        Enumeration rentalss = rentals.elements();
        String result = "Rental Record for" +" "+ getName()+"\n";
        while(rentalss.hasMoreElements())
        {
            double thisAmount = 0;
            Rental each = (Rental) rentalss.nextElement();

            thisAmount = amountFor(each);  //提炼方法  
            /*switch(each.getMovie().getPriceCode())
            {
                case Movie.regular:
                thisAmount += 2;
                if(each.getDayRented()>2)
                    thisAmount += (each.getDayRented()-2)*1.5; 
                break;

                case Movie.new_release:
                thisAmount += each.getDayRented()*3;
                break;

                case Movie.childrens:
                    thisAmount += 1.5;
                    if(each.getDayRented()>3)
                        thisAmount += (each.getDayRented()-3)*1.5; 
                    break;
            }*/

            //积分  每借一张加1个积分
            frequentRenterPoints++;
            //积分累加条件  新版本的片子,借的时间大于1天
            if((each.getMovie().getPriceCode()==Movie.new_release)&&each.getDayRented()>1)
            {
                frequentRenterPoints++;
            }

            result +="\t" +each.getMovie().getTitle()+"\t" 
                    +String.valueOf(thisAmount)+"\n";

            totalAmount += thisAmount;
        }

        result += "Amount owed is "+ String.valueOf(totalAmount)+"\n";
        result +="You earned "+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.getDayRented()>2)
                thisAmount += (each.getDayRented()-2)*1.5; 
            break;

            case Movie.new_release:
            thisAmount += each.getDayRented()*3;
            break;

            case Movie.childrens:
                thisAmount += 1.5;
                if(each.getDayRented()>3)
                    thisAmount += (each.getDayRented()-3)*1.5; 
                break;
        }

        return thisAmount;
    }


}

备注:《Refactoring—Improving the Design of the Existing Code》书中说SmallTalk编译器具备重构功能,尝试使用Myeclipse 2015编译器使用。

操作过程:
(1) 选中需要重构的代码片段;
(2) IDE编译器的功能导航;
《重构-改善代码既有的设计》重构,第一个案例_第1张图片

(3) 根据IDE编译器对话框提示;
《重构-改善代码既有的设计》重构,第一个案例_第2张图片

(4)用Myeclipse 2015编译器自动重构的代码如下,用Client客户端调用结果一致,修改后Customer代码:

package com.lee.test.aFirstExample;

import java.util.Enumeration;
import java.util.Vector;

public class Customer {
    /**
     * @param name
     */
    public Customer(String name) {
        super();
        this.name = name;
    }
    private String name;
    public String getName() {
        return name;
    }
    private Vector rentals = new Vector();

    public void addRental(Rental arg)
    {
        rentals.addElement(arg);
    }

    public String statement(){
        double totalAmount = 0;
        int frequentRenterPoints = 0;
        Enumeration rentalss = rentals.elements();
        String result = "Rental Record for" +" "+ getName()+"\n";
        while(rentalss.hasMoreElements())
        {
            double thisAmount = 0;
            Rental each = (Rental) rentalss.nextElement();

    //      thisAmount = amountFor(each);  //提炼方法  
            thisAmount = amountFor(thisAmount, each);

            //积分  每借一张加1个积分
            frequentRenterPoints++;
            //积分累加条件  新版本的片子,借的时间大于1天
            if((each.getMovie().getPriceCode()==Movie.new_release)&&each.getDayRented()>1)
            {
                frequentRenterPoints++;
            }

            result +="\t" +each.getMovie().getTitle()+"\t" 
                    +String.valueOf(thisAmount)+"\n";

            totalAmount += thisAmount;
        }

        result += "Amount owed is "+ String.valueOf(totalAmount)+"\n";
        result +="You earned "+String.valueOf(frequentRenterPoints)+" "
                +"frequent renter points";
        return result;
    }

    /**
     * @param thisAmount
     * @param each
     * @return
     */
    private double amountFor(double thisAmount, Rental each) {
        switch(each.getMovie().getPriceCode())
        {
            case Movie.regular:
            thisAmount += 2;
            if(each.getDayRented()>2)
                thisAmount += (each.getDayRented()-2)*1.5; 
            break;

            case Movie.new_release:
            thisAmount += each.getDayRented()*3;
            break;

            case Movie.childrens:
                thisAmount += 1.5;
                if(each.getDayRented()>3)
                    thisAmount += (each.getDayRented()-3)*1.5; 
                break;
        }
        return thisAmount;
    }

    /*private double amountFor(Rental each)
    {
        double thisAmount = 0;
        switch(each.getMovie().getPriceCode())
        {
            case Movie.regular:
            thisAmount += 2;
            if(each.getDayRented()>2)
                thisAmount += (each.getDayRented()-2)*1.5; 
            break;

            case Movie.new_release:
            thisAmount += each.getDayRented()*3;
            break;

            case Movie.childrens:
                thisAmount += 1.5;
                if(each.getDayRented()>3)
                    thisAmount += (each.getDayRented()-3)*1.5; 
                break;
        }

        return thisAmount;
    }*/



}

7、重构第二步
移动方法(Move Method)
将amountFor()方法从Customer类中移动到Rental类中,并修改方法名称为getCharge()。

移动amountFor()方法后的Customer代码:

package com.lee.test.aFirstExample;

import java.util.Enumeration;
import java.util.Vector;

public class Customer {
    /**
     * @param name
     */
    public Customer(String name) {
        super();
        this.name = name;
    }
    private String name;
    public String getName() {
        return name;
    }
    private Vector rentals = new Vector();

    public void addRental(Rental arg)
    {
        rentals.addElement(arg);
    }

    public String statement(){
        double totalAmount = 0;
        int frequentRenterPoints = 0;
        Enumeration rentalss = rentals.elements();
        String result = "Rental Record for" +" "+ getName()+"\n";
        while(rentalss.hasMoreElements())
        {
            double thisAmount = 0;
            Rental each = (Rental) rentalss.nextElement();

    //      thisAmount = amountFor(each);  //提炼方法  
            thisAmount = each.getCharge(thisAmount);

            //积分  每借一张加1个积分
            frequentRenterPoints++;
            //积分累加条件  新版本的片子,借的时间大于1天
            if((each.getMovie().getPriceCode()==Movie.new_release)&&each.getDayRented()>1)
            {
                frequentRenterPoints++;
            }

            result +="\t" +each.getMovie().getTitle()+"\t" 
                    +String.valueOf(thisAmount)+"\n";

            totalAmount += thisAmount;
        }

        result += "Amount owed is "+ String.valueOf(totalAmount)+"\n";
        result +="You earned "+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.getDayRented()>2)
                thisAmount += (each.getDayRented()-2)*1.5; 
            break;

            case Movie.new_release:
            thisAmount += each.getDayRented()*3;
            break;

            case Movie.childrens:
                thisAmount += 1.5;
                if(each.getDayRented()>3)
                    thisAmount += (each.getDayRented()-3)*1.5; 
                break;
        }

        return thisAmount;
    }*/



}

移动amountFor()方法后的Rental代码:

package com.lee.test.aFirstExample;

public class Rental {
    /**
     * @param movie
     * @param dayRented
     */
    public Rental(Movie movie, int dayRented) {
        super();
        this.movie = movie;
        this.dayRented = dayRented;
    }

    private Movie movie;
    public Movie getMovie() {
        return movie;
    }

    private int dayRented;
    public int getDayRented() {
        return dayRented;
    }
    /**
     * @param thisAmount
     * @return
     */
    double getCharge(double thisAmount) {
        switch(getMovie().getPriceCode())
        {
            case Movie.regular:
            thisAmount += 2;
            if(getDayRented()>2)
                thisAmount += (getDayRented()-2)*1.5; 
            break;

            case Movie.new_release:
            thisAmount += getDayRented()*3;
            break;

            case Movie.childrens:
                thisAmount += 1.5;
                if(getDayRented()>3)
                    thisAmount += (getDayRented()-3)*1.5; 
                break;
        }
        return thisAmount;
    }


}

8、重构第三步
以查询取代临时变量(Replace temp with query)
除去临时变量thisAmount;
修改后的statement账单方法:

public String statement(){
        double totalAmount = 0;
        int frequentRenterPoints = 0;
        Enumeration rentalss = rentals.elements();
        String result = "Rental Record for" +" "+ getName()+"\n";
        while(rentalss.hasMoreElements())
        {
            Rental each = (Rental) rentalss.nextElement();

    //      thisAmount = amountFor(each);  //提炼方法  

            //积分  每借一张加1个积分
            frequentRenterPoints++;
            //积分累加条件  新版本的片子,借的时间大于1天
            if((each.getMovie().getPriceCode()==Movie.new_release)&&each.getDayRented()>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 earned "+String.valueOf(frequentRenterPoints)+" "
                +"frequent renter points";
        return result;
    }

修改Rental类的getCharge()方法,由传参改为内部变量:

    double getCharge() {
        double result = 0;
        switch(getMovie().getPriceCode())
        {
            case Movie.regular:
                result += 2;
            if(getDayRented()>2)
                result += (getDayRented()-2)*1.5; 
            break;

            case Movie.new_release:
                result += getDayRented()*3;
            break;

            case Movie.childrens:
                result += 1.5;
                if(getDayRented()>3)
                    result += (getDayRented()-3)*1.5; 
                break;
        }
        return result;
    }

9、重构第四步:
提取方法提取“常客积分计算”:

    int getFrequentRenterPoints() {
        //积分累加条件  新版本的片子,借的时间大于1天
        if((getMovie().getPriceCode()==Movie.new_release)&&getDayRented()>1)
        {
            return 2;
        }
        return 1;
    }

10、重构第五步:
移动方法(Move Method)
将getFrequentRenterPoints()方法从Customer类中移动到Rental类中。

11、重构第六步:
以查询取代临时变量(Replace temp with query)
除去临时变量thisAmount;
除去临时变量frequentRenterPoints;
修改后的Customer类代码:

public String statement(){
        Enumeration rentalss = rentals.elements();
        String result = "Rental Record for" +" "+ getName()+"\n";
        while(rentalss.hasMoreElements())
        {
            Rental each = (Rental) rentalss.nextElement();          
            result +="\t" +each.getMovie().getTitle()+"\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;
    }


    private int getTotalFrequentRenterPoints() {
        int points = 0;
        Enumeration rentalss = rentals.elements();
        while(rentalss.hasMoreElements())
        {
            Rental each = (Rental) rentalss.nextElement();
            points += each.getFrequentRenterPoints();
        }

        return points;
    }

    private double getTotalCharge()
    {
        double result = 0;
        Enumeration rentalss = rentals.elements();
        while(rentalss.hasMoreElements())
        {
            Rental each = (Rental) rentalss.nextElement();
            result += each.getCharge();
        }
        return result;
    }

12、重构第七步:
将影片相关的费用、积分方法Move到Movie类中,将Rental的属性daysRented作为参数。
修改后的Rental类:

package com.lee.test.aFirstExample;

public class Rental {
    /**
     * @param movie
     * @param dayRented
     */
    public Rental(Movie movie, int dayRented) {
        super();
        this.movie = movie;
        this.dayRented = dayRented;
    }

    private Movie movie;
    public Movie getMovie() {
        return movie;
    }

    private int dayRented;
    public int getDayRented() {
        return dayRented;
    }


}

修改后的Movie类:

package com.lee.test.aFirstExample;

public class Movie {

    /**
     * @param title
     * @param priceCode
     */
    public Movie(String title, int priceCode) {
        super();
        this.title = title;
        this.priceCode = priceCode;
    }

    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 String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
    public int getPriceCode() {
        return priceCode;
    }
    public void setPriceCode(int priceCode) {
        this.priceCode = priceCode;
    }
    /**
     * @param daysRented
     * @return
     */
    double getCharge(int daysRented) {
        double result = 0;
        switch(getPriceCode())
        {
            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;
    }
    /**
     * @param daysRented 
     * @return
     */
    int getFrequentRenterPoints(int daysRented) {
        //积分累加条件  新版本的片子,借的时间大于1天
        if((getPriceCode()== Movie.new_release) && daysRented>1)
        {
            return 2;
        }
        return 1;
    }


}

修改后的Customer类:

package com.lee.test.aFirstExample;

import java.util.Enumeration;
import java.util.Vector;

public class Customer {
    /**
     * @param name
     */
    public Customer(String name) {
        super();
        this.name = name;
    }
    private String name;
    public String getName() {
        return name;
    }
    private Vector rentals = new Vector();

    public void addRental(Rental arg)
    {
        rentals.addElement(arg);
    }

    public String statement(){
        Enumeration rentalss = rentals.elements();
        String result = "Rental Record for" +" "+ getName()+"\n";
        while(rentalss.hasMoreElements())
        {
            Rental each = (Rental) rentalss.nextElement();          
            result +="\t" +each.getMovie().getTitle()+"\t" 
                    +String.valueOf(each.getMovie().getCharge(each.getDayRented()))+"\n";
        }

        result += "Amount owed is "+ String.valueOf(getTotalCharge())+"\n";
        result +="You earned "+String.valueOf(getTotalFrequentRenterPoints())+" "
                +"frequent renter points";
        return result;
    }


    private int getTotalFrequentRenterPoints() {
        int points = 0;
        Enumeration rentalss = rentals.elements();
        while(rentalss.hasMoreElements())
        {
            Rental each = (Rental) rentalss.nextElement();
            points += each.getMovie().getFrequentRenterPoints(each.getDayRented());
        }

        return points;
    }

    private double getTotalCharge()
    {
        double result = 0;
        Enumeration rentalss = rentals.elements();
        while(rentalss.hasMoreElements())
        {
            Rental each = (Rental) rentalss.nextElement();
            result += each.getMovie().getCharge(each.getDayRented());
        }
        return result;
    }

}

13、重构第八步:
运用多态取代与价格相关的条件逻辑。
创建Price价格类,作为抽象类给多种影片类型继承,关联到Movie影片类中,Price类:

package com.lee.test.aFirstExample;

public abstract class Price {
    abstract int getPriceCode();

    abstract double getCharge(int daysRented);
}

分别创建3个不同价格类型的类:
NewReleasePrice类:

package com.lee.test.aFirstExample;

public class NewReleasePrice extends Price{

    @Override
    int getPriceCode() {

        return Movie.new_release;
    }

    double getCharge(int daysRented)
    {
        double result = 0;
        result += daysRented*3;
        return result;
    }

}

ChildrensPrice 类:

package com.lee.test.aFirstExample;

public class ChildrensPrice extends Price{

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

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


}

RegularPrice 类:

package com.lee.test.aFirstExample;

public class RegularPrice extends Price{

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

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

}

修改后的Movie类:

package com.lee.test.aFirstExample;

public class Movie {

    /**
     * @param title
     * @param priceCode
     */
    public Movie(String title, int priceCode) {
        super();
        this.title = title;
        this.priceCode = priceCode;
    }

    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;
    private Price price;



    public Price getPrice() {
        return price;
    }
    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
    public int getPriceCode() {
        return price.getPriceCode();
    }
    public void setPriceCode(int arg) {
        switch(arg)
        {
        case regular:
            price = new RegularPrice();
            break;
        case new_release:
            price = new NewReleasePrice();
            break;
        case childrens:
            price = new ChildrensPrice();
            break;
        default:
            throw new IllegalArgumentException("Incorrect Price Code");
        }
    }

    /**
     * @param daysRented 
     * @return
     */
    int getFrequentRenterPoints(int daysRented) {
        //积分累加条件  新版本的片子,借的时间大于1天
        if((getPriceCode()== Movie.new_release) && daysRented>1)
        {
            return 2;
        }
        return 1;
    }


}

Rental类:

package com.lee.test.aFirstExample;

public class Rental {
    /**
     * @param movie
     * @param dayRented
     */
    public Rental(Movie movie, int dayRented) {
        super();
        this.movie = movie;
        this.dayRented = dayRented;
    }

    private Movie movie;
    public Movie getMovie() {
        return movie;
    }

    private int dayRented;
    public int getDayRented() {
        return dayRented;
    }


}

Customer类:

package com.lee.test.aFirstExample;

import java.util.Enumeration;
import java.util.Vector;

public class Customer {
    /**
     * @param name
     */
    public Customer(String name) {
        super();
        this.name = name;
    }
    private String name;
    public String getName() {
        return name;
    }
    private Vector rentals = new Vector();

    public void addRental(Rental arg)
    {
        rentals.addElement(arg);
    }

    public String statement(){
        Enumeration rentalss = rentals.elements();
        String result = "Rental Record for" +" "+ getName()+"\n";
        while(rentalss.hasMoreElements())
        {
            Rental each = (Rental) rentalss.nextElement();          
            result +="\t" +each.getMovie().getTitle()+"\t" 
                    +String.valueOf(each.getMovie().getPrice().getCharge(each.getDayRented()))+"\n";
        }

        result += "Amount owed is "+ String.valueOf(getTotalCharge())+"\n";
        result +="You earned "+String.valueOf(getTotalFrequentRenterPoints())+" "
                +"frequent renter points";
        return result;
    }


    private int getTotalFrequentRenterPoints() {
        int points = 0;
        Enumeration rentalss = rentals.elements();
        while(rentalss.hasMoreElements())
        {
            Rental each = (Rental) rentalss.nextElement();
            points += each.getMovie().getFrequentRenterPoints(each.getDayRented());
        }

        return points;
    }

    private double getTotalCharge()
    {
        double result = 0;
        Enumeration rentalss = rentals.elements();
        while(rentalss.hasMoreElements())
        {
            Rental each = (Rental) rentalss.nextElement();
            result += each.getMovie().getPrice().getCharge(each.getDayRented());
        }
        return result;
    }



}

运行的Client:

package com.lee.test.aFirstExample;

public class Client {

    public static void main(String[] args) {


        Movie mov1 = new Movie("metal",2);
        mov1.setPriceCode(2);
        Rental ren = new Rental(mov1,8);

        Movie mov2 = new Movie("apple",1);
        mov2.setPriceCode(1);
        Rental xxx = new Rental(mov2,6);

        Customer cus = new Customer("Lee");
        cus.addRental(ren);
        cus.addRental(xxx);

        System.out.println(cus.statement());


    }


}

打印输出结果:

Rental Record for Lee
    metal   9.0
    apple   18.0
Amount owed is 27.0
You earned 3 frequent renter points

你可能感兴趣的:(UML,&,Structure,Refactoring)