1. 程式人生 > >重構-改善既有程式碼的設計(一)--重構第一個案例

重構-改善既有程式碼的設計(一)--重構第一個案例

什麼是重構

在不改變程式碼外在行為的前提下,對程式碼做出修改以改程序序內部的結構
簡單地說就是在程式碼寫好後改進它的設計

誰該閱讀這本書

  1. 專業程式設計師(能夠提高你的程式碼質量)
  2. 資深設計師和架構規劃師(理解為什麼需要重構,哪裡需要重構)

閱讀技巧

帶著疑問去讀:
- 如果你想要知道重構是什麼。第1章夠了
- 如果你想要知道為什麼要重構,第1,2章
- 如果你想知道該在什麼地方重構,第3章
- 如果你想進行重構,第1,2,3,4章。並根據目錄選讀

第1章 重構,第一個案例

public String statement(){

        double totalAmount=0;
        int frequentRenterPoints=0;
        Enumeration<Rental> rentals = _rentals.elements();

        String result = "Rental Record for "+getName()+"\n";
        while(rentals.hasMoreElements()){

            double thisAmount=0;
            Rental each = (Rental)rentals.nextElement();

            switch (each.getMovie().getPriceCode()) {
            case Movie.CHILDRENS:

                thisAmount += 1.5;
                if(each.getDaysRented()>3){
                    thisAmount += (each.getDaysRented()-3)*1.5;
                }
                break;
            case Movie.NEW_RELEASE:

                thisAmount += each.getDaysRented()*3;
                break;
            case Movie.REGULAR:

                thisAmount += 2;
                if(each.getDaysRented()>2){
                    thisAmount += (each.getDaysRented()-2)*1.5;
                }
                break;

            default:
                break;
            }

            frequentRenterPoints++;

            if(each.getMovie().getPriceCode()==Movie.NEW_RELEASE && each.getDaysRented()>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;
    }

這是隻是一個方法。直接評價:太複雜,複用率低

解決方法

分解並重組

將程式碼按照功能拆分。每個功能只做一件事。

拆除switch並封裝為方法

private double amountFor(Rental each, double result) {
    switch (each.getMovie().getPriceCode()) {
    case Movie.CHILDRENS:

        result += 1.5;
        if(each.getDaysRented()>3){
            result += (each.getDaysRented()-3)*1.5;
        }
        break;
    case Movie.NEW_RELEASE:

        result += each.getDaysRented()*3;
        break;
    case Movie.REGULAR:

        result += 2;
        if(each.getDaysRented()>2){
            result += (each.getDaysRented()-2)*1.5;
        }
        break;

    default:
        break;
    }
    return result;
}

重設變數名

變數名必須保證簡單清楚,不產生歧義。比如上方程式碼的each就是可修改的變數名。因為each到底指的是什麼

搬移程式碼

因為這個方法只使用了rental的資訊。所以需要放在rental類下

去除臨時變數

statement方法中有兩個臨時變數totalAmount和frequentRenterPoints。 做成getTotalAmount和getFrequentRenterPoints方法

public String statement() {
        double totalAmount = 0;
        int frequentRenterPoints = 0;
        // Enumeration介面定義了從一個數據結構得到連續資料的手段
        Enumeration rentals = _rentals.elements();

        String result = "Rental Record for" + getName() + "\n";
        while (rentals.hasMoreElements()) {
            Rental each = (Rental) rentals.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 result=0;
    Enumeration rentals=_rentals.elements();
    while(rentals.hasMoreElements()){
        Rental each=(Rental)rentals.nextElement();
        result+=each.getMovie().getFrequentRenterPoints(each.getDayRented());
    }
    return result;
}
private double getTotalCharge(){
    double result =0;
    Enumeration rentals=_rentals.elements();
    while(rentals.hasMoreElements()){
        Rental each=(Rental)rentals.nextElement();
        result+=each.getMovie().getPrice().getCharge(each.getDayRented());
    }
    return result;
}

測試重構後的方法

保證重構後的方法能夠滿足所有的需求

總結

  • 程式碼塊俞小,程式碼的功能就俞容易管理,程式碼的處理和移動也就俞輕鬆
  • 任何不會被修改的變數都可以被當成引數傳入新的函式,至於會被修改的變數需要慎重。如果只有一個變數會被修改,可以把它當做返回值。
  • 絕大多數情況下,函式應該放在它所使用的資料的所屬物件內
  • 最好不要在另一個物件的屬性基礎上運用switch語句。如果不得不使用,也應該在物件自己的資料上使用,而不是在別人的資料上使用。
  • 使用繼承來適當組織類關係後,可以用多型取代switch語句。