重构 demo篇

 本文背景为学习重构一书中的一些信息的记录。

该篇以一个影片租赁为背景。通过一层层的重构,最终实现相对来说比较完美的代码。

文中经典语句摘要:

如果它没有坏,就不要动它 。” 用来形容我们工作中一些比较古董级的代码,或许写的很烂,但是基本功能是OK的,对于一个团队leader来说,不会主张你去修改他,对于一个保守级的开发来说,也懒的去修改他,虽然你个人任务修改它出错的概率近乎为0,但是仍旧是近乎。只愿我能在工作中永远保持一个敬畏之心,而非畏惧之心。

1、第一版,较多的信息冗余在一起,相对书中的第一版已经有了一些提升,加上了个人在初步做的一些重构,怎么说哥也是写过两年代码的人。简单重构还是了解一些的。

 用户类,定义用户的名称以及租赁了哪些影片


package
com.woniu.refactoring; import java.util.Enumeration; import java.util.Vector; public class Customer { private String _name; private Vector _rentals = new Vector(); public String getName() { return _name; } public void setName(String _name) { this._name = _name; } public Vector getRentals() { return _rentals; } public void setRentals(Vector _rentals) { this._rentals = _rentals; } public String statement() { double totalAmout = 0; int frequentRenterPoints = 0; Enumeration rentals = _rentals.elements(); String result = "Rental Record for " + getName() + "\n"; while(rentals.hasMoreElements()) { double thisAmout = 0; Rental each = (Rental) rentals.nextElement(); //这里是个人阅读到这里时,认为需要重构的位置。用专业点的话叫提炼函数 TODO // switch (each.getMovie().getPriceCode()) { // case Movie.REGULAR: // thisAmout += 2; // if(each.getDaysRented() > 2) { // thisAmout += (each.getDaysRented() - 2) * 1.5; // } // break; // case Movie.NEW_RELEASE: // thisAmout += each.getDaysRented() * 3; // break; // // case Movie.CHILDENS: // thisAmout += 1.5; // if(each.getDaysRented() > 3) { // thisAmout += (each.getDaysRented() - 3) * 1.5; // } // break; // } //上述方法,经过提炼后得到这个相对来说可以重用的一个函数amounFor thisAmout = amountFor(each); //这里是个人阅读到这里时,认为需要重构的位置。用专业点的话叫提炼函数 TODO // frequentRenterPoints ++; // if((each.getMovie().getPriceCode() == Movie.NEW_RELEASE) && each.getDaysRented() > 1) { // frequentRenterPoints ++; // } //进过提炼后的积分计算方式 frequentRenterPoints = getFrequentRenterPoints(each); result += "\t" + each.getMovie().getTitle() + "\t" + String.valueOf(thisAmout) + "\n"; totalAmout += thisAmout; } result += "Amount owed is " + String.valueOf(totalAmout) + "\n"; result += "You earned " + String.valueOf(frequentRenterPoints) + " frequent renter points"; return result; } /** * 用于计算此次租赁产生的积分 * @param rental 租赁 * @return */ private int getFrequentRenterPoints(Rental rental) { if((rental.getMovie().getPriceCode() == Movie.NEW_RELEASE) && rental.getDaysRented() > 1) { return 2;//这里其实在最初我是没有想到可以直接用2来代替的 } return 1; } /** * 用于计算此次租赁消费金额 * @param rental * @return */ private double amountFor(Rental rental) { double thisAmout = 0; switch (rental.getMovie().getPriceCode()) { case Movie.REGULAR: thisAmout += 2; if(rental.getDaysRented() > 2) { thisAmout += (rental.getDaysRented() - 2) * 1.5; } break; case Movie.NEW_RELEASE: thisAmout += rental.getDaysRented() * 3; break; case Movie.CHILDENS: thisAmout += 1.5; if(rental.getDaysRented() > 3) { thisAmout += (rental.getDaysRented() - 3) * 1.5; } break; } return thisAmout; } }

 影片类,定义了影片的类型以及价格

package com.woniu.refactoring;

public class Movie {
    public static final int CHILDENS = 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) {
        _title = title;
        priceCode = priceCode;
    }
    
    public int getPriceCode() {
        return _priceCode;
    }
    public void setPriceCode(int _priceCode) {
        this._priceCode = _priceCode;
    }
    public String getTitle() {
        return _title;
    }
    
    
}

 租赁类,记录影片被租赁的时间以及影片信息

package com.woniu.refactoring;

public class Rental {
    private Movie _movie;
    private int _daysRented;
    
    public Rental(Movie movie, int daysRented) {
        _movie = movie;
        _daysRented = daysRented;
    }
    
    public Movie getMovie() {
        return _movie;
    }
    public int getDaysRented() {
        return _daysRented;
    }
    
    
}

 第二版

由于在amountFor()方法中,并没有任何用户的信息,却在用户的类中实现了关于影片计算逻辑,金额的产生发生的位置应该是在租赁中产生的,因此,应该将其移至Rental中,使用(Move Mehtod)来进行处理。

用户类,封装了用户总消费额以及总积分的计算。同时,将关于租赁的单价计算与单积分计算进行了迁移。

package com.woniu.refactoring;

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

public class Customer {
    private String _name;
    private Vector _rentals = new Vector();
    
    public String getName() {
        return _name;
    }
    public void setName(String _name) {
        this._name = _name;
    }
    public Vector getRentals() {
        return _rentals;
    }
    public void setRentals(Vector _rentals) {
        this._rentals = _rentals;
    }
    
    
    public String statement() {
//        double totalAmout = 0;
//        int frequentRenterPoints = 0;
        Enumeration rentals = _rentals.elements();
        String result = "Rental Record for " + getName() + "\n";
        while(rentals.hasMoreElements()) {
            double thisAmout = 0;
            Rental each = (Rental) rentals.nextElement();
            //这里是个人阅读到这里时,认为需要重构的位置。用专业点的话叫提炼函数 TODO
//            switch (each.getMovie().getPriceCode()) {
//            case Movie.REGULAR:
//                thisAmout += 2;
//                if(each.getDaysRented() > 2) {
//                    thisAmout += (each.getDaysRented() - 2) * 1.5;
//                }
//                break;
//            case Movie.NEW_RELEASE:
//                thisAmout += each.getDaysRented() * 3;
//                break;
//
//            case Movie.CHILDENS:
//                thisAmout += 1.5;
//                if(each.getDaysRented() > 3) {
//                    thisAmout += (each.getDaysRented() - 3) * 1.5;
//                }
//                break;
//            }
            //上述方法,经过提炼后得到这个相对来说可以重用的一个函数amounFor
//            thisAmout = amountFor(each);
            thisAmout = each.getCharge();//这是第二版的重构,将金额计算与积分计算,移至了Rental中,因为这是在租赁过程中产生的
            //这里是个人阅读到这里时,认为需要重构的位置。用专业点的话叫提炼函数 TODO
//            frequentRenterPoints ++;
//            if((each.getMovie().getPriceCode() == Movie.NEW_RELEASE) && each.getDaysRented() > 1) {
//                frequentRenterPoints ++;
//            }
            //进过提炼后的积分计算方式
//            frequentRenterPoints = getFrequentRenterPoints(each);
//            frequentRenterPoints = each.getFrequentRenterPoints();//这是第二版的重构,将金额计算与积分计算,移至了Rental中,因为这是在租赁过程中产生的
            result += "\t" + each.getMovie().getTitle() + "\t" + String.valueOf(thisAmout) + "\n";
//            totalAmout += thisAmout;//2书中提到该处可以直接使用getCharge()获取金额,进而避免了局部变量的使用,但是,我没有找到关于多次计算的优化点,因此不敢王嘉苟同。
            
        }
        
        result += "Amount owed is " + String.valueOf(getTotalCharge()) + "\n";//对于总金额计算单独提取方法,以备后用
        result += "You earned " + String.valueOf(getTotalFrequentRenterPoints()) + " frequent renter points";
        return result;
    }
    
    /**
     * 获取用户消费的总金额
     * @return
     */
    private double getTotalCharge() {
        double totalAmout = 0;
        Enumeration rentals = _rentals.elements();
        while(rentals.hasMoreElements()) {
            Rental rental = (Rental)rentals.nextElement();
            totalAmout += rental.getCharge();
        }
        return totalAmout;
    }
    
    /**
     * 获取用户消费的总积分
     * @return
     */
    private int getTotalFrequentRenterPoints() {
        int result = 0;
        Enumeration rentals = _rentals.elements();
        while(rentals.hasMoreElements()) {
            Rental rental = (Rental)rentals.nextElement();
            result += rental.getFrequentRenterPoints();
        }
        return result;
    }
    
    /**
     * 用于计算此次租赁产生的积分
     * @param rental 租赁
     * @return
     */
    @Deprecated
    private int getFrequentRenterPoints(Rental rental) {
        if((rental.getMovie().getPriceCode() == Movie.NEW_RELEASE) && rental.getDaysRented() > 1) {
            return 2;//这里其实在最初我是没有想到可以直接用2来代替的
        }
        return 1;
    }
    
    /**
     * 用于计算此次租赁消费金额
     * @param rental
     * @return
     */
    @Deprecated
    private double amountFor(Rental rental) {
        return rental.getCharge();//这里其实没必要再次进行封装函数了。可以直接在循环中使用rental的getCharge方法进行计算。
//        double thisAmout = 0;
//        switch (rental.getMovie().getPriceCode()) {
//        case Movie.REGULAR:
//            thisAmout += 2;
//            if(rental.getDaysRented() > 2) {
//                thisAmout += (rental.getDaysRented() - 2) * 1.5;
//            }
//            break;
//        case Movie.NEW_RELEASE:
//            thisAmout += rental.getDaysRented() * 3;
//            break;
//
//        case Movie.CHILDENS:
//            thisAmout += 1.5;
//            if(rental.getDaysRented() > 3) {
//                thisAmout += (rental.getDaysRented() - 3) * 1.5;
//            }
//            break;
//        }
//        return thisAmout;
    }
    
}

 租赁类,增加了关于租赁单价的计算以及积分计算。

package com.woniu.refactoring;

public class Rental {
    private Movie _movie;
    private int _daysRented;
    
    public Rental(Movie movie, int daysRented) {
        _movie = movie;
        _daysRented = daysRented;
    }
    
    public Movie getMovie() {
        return _movie;
    }
    public int getDaysRented() {
        return _daysRented;
    }
    
    /**
     * 用户此次租赁积分的计算
     * @param rental
     * @return
     */
    public int getFrequentRenterPoints() {
        if((getMovie().getPriceCode() == Movie.NEW_RELEASE) && getDaysRented() > 1) {
            return 2;//这里其实在最初我是没有想到可以直接用2来代替的
        }
        return 1;
    }
    
    
    /**
     * 用于计算此次租赁消费金额
     * 2关于这个方法,其实应该是在租赁关系发生时,已经需要计算好金额了。这样就可以避免了Customer中提到的优化点,
     * 但是,这样同样会造成如果交易取消了,我浪费资源进行了计算。
     * @param rental
     * @return
     */
    public double getCharge() {
        double thisAmout = 0;
        switch (getMovie().getPriceCode()) {
        case Movie.REGULAR:
            thisAmout += 2;
            if(getDaysRented() > 2) {
                thisAmout += (getDaysRented() - 2) * 1.5;
            }
            break;
        case Movie.NEW_RELEASE:
            thisAmout += getDaysRented() * 3;
            break;

        case Movie.CHILDENS:
            thisAmout += 1.5;
            if(getDaysRented() > 3) {
                thisAmout += (getDaysRented() - 3) * 1.5;
            }
            break;
        }
        return thisAmout;
    }
    
}

 原本到这里,我理解,此次重构已经结束了。但发现,后续还有新的重构方向。。。。。

猜你喜欢

转载自www.cnblogs.com/woniu4/p/9271532.html