重構CH1//重構第二步

提煉「常客積點計算」程式碼

承第一步的最後一段程式
在statement()裡面的這一段
這一段積點計算程式,應該放在Rental class身上,運用Extract Method

inline std::string Customer::statement()
{
    double totalAmount = 0;       //消費總金額
    int frequentReterPoints = 0;  //常客積點
    std::vector::const_iterator rentals = _rentals.begin();
    std::string result = "Rental Record for " + getName() + "\n";

    while(retals.hasMoreElements())
    {
        rentals++;
        Rental each = *rentals;    //取得一筆租借記錄
//----------------------------------------------------------------------------
        //add frequent renter points(累加 常客積點)
        frequentReterPoints++;
        //add bonus for a two day new release rental
        if ((each.getMovie().getPriceCode() == Movie.NEW.RELEASE) && each.getDayRented() > 1))
            frequentReterPoints++;
//----------------------------------------------------------------------------
        //show figures for this rental (顯示這筆租借資料)
        result += "\t" + each.getMovie().getTitle() + "\t" + std::string.valueOf(each.getCharge()) + "\n";
        totalAmount += each.getCharge();
    }

    //add footer lines(結尾列印)
    result += "Amount owed is " + std::string.valueOf(totalAmount) + "\n";
    rental += "You earned " + std::string.valueOf(frequentReterPoints) + " frequent renter points";

    return result;
}
再一次尋找區域變數。

再這找到了each和fequentRenterPoints
使用前,有初值,提出來的函式不需傳入該值
只要做appending assignment, operator +=的動作
inline std::string Customer::statement()
{
    double totalAmount = 0;       //消費總金額
    int frequentReterPoints = 0;  //常客積點
    std::vector::const_iterator rentals = _rentals.begin();
    std::string result = "Rental Record for " + getName() + "\n";

    while(retals.hasMoreElements())
    {
        rentals++;
        Rental each = *rentals;    //取得一筆租借記錄
//----------------------------------------------------------------------------
        frequentReterPoints += each.getFrequentRenterPoints();
//----------------------------------------------------------------------------
        //show figures for this rental (顯示這筆租借資料)
        result += "\t" + each.getMovie().getTitle() + "\t" + std::string.valueOf(each.getCharge()) + "\n";
        totalAmount += each.getCharge();
    }

    //add footer lines(結尾列印)
    result += "Amount owed is " + std::string.valueOf(totalAmount) + "\n";
    rental += "You earned " + std::string.valueOf(frequentReterPoints) + " frequent renter points";

    return result;
}
提煉出來...變成
inline int Rental::getFrequentRenterPoints()
{
    if ((getMovie().getPriceCode() == Movie.NEW.RELEASE) && getDayRented() > 1))
        return 2;
    else 
        return 1;
}

去除暫時變數


運用Replace Temp with Query
用query method來取代totalAmount和frequentRenterPoint這兩個暫時的變數
query method可以促進較乾淨的設計。和
inline std::string Customer::statement()
{
//    double totalAmount = 0;       //消費總金額
    int frequentReterPoints = 0;  //常客積點
    std::vector<rental>::const_iterator rentals = _rentals.begin();
    std::string result = "Rental Record for " + getName() + "\n";

    while(retals.hasMoreElements())
    {
        rentals++;
        Rental each = *rentals;    //取得一筆租借記錄
        frequentReterPoints += each.getFrequentRenterPoints();

        //show figures for this rental (顯示這筆租借資料)
        result += "\t" + each.getMovie().getTitle() + "\t" + std::string.valueOf(each.getCharge()) + "\n";
//        totalAmount += each.getCharge();
    }
    //add footer lines(結尾列印)
    result += "Amount owed is " + std::string.valueOf(getTotalCharge()) + "\n";
                                   //totalsAmount 改成 getTotalCharge()
    rental += "You earned " + std::string.valueOf(frequentReterPoints) + " frequent renter points";

    return result;
}
這就是所謂的query method
把totalAmount整個搬走,包含回圈內的變動。
inline double Customer::getTotalCharge()
{
    double result = 0;
    std::vector<rental>::const_iterator rentals = _rentals.begin();
    while (rentals.hasMoreElements())
    {
        rentals++;
        Rental each = *rentals;    //取得一筆租借記錄
        result += each.getCharge();
    }
    return result;
}
測試完,沒問題之後,再接著處理frequentReterPoints
statement()改成這樣
inline std::string Customer::statement()
{
//    double totalAmount = 0;       //消費總金額
//    int frequentReterPoints = 0;  //常客積點
    std::vector::const_iterator rentals = _rentals.begin();
    std::string result = "Rental Record for " + getName() + "\n";

    while(retals.hasMoreElements())
    {
        rentals++;
        Rental each = *rentals;    //取得一筆租借記錄
//        frequentReterPoints += each.getFrequentRenterPoints();

        //show figures for this rental (顯示這筆租借資料)
        result += "\t" + each.getMovie().getTitle() + "\t" + std::string.valueOf(each.getCharge()) + "\n";
//        totalAmount += each.getCharge();
    }
    //add footer lines(結尾列印)
    result += "Amount owed is " + std::string.valueOf(getTotalCharge()) + "\n";
    rental += "You earned " + std::string.valueOf(getTotalFrequentReterPoints()) + " frequent renter points";

    return result;
}
再把frequentReterPoints整個搬走
inline int getTotalFrequentReterPoints()
{
    int result = 0;
    std::vector::const_iterator rentals = _rentals.begin();
    while (rentals.hasMoreElements())
    {
        rentals++;
        Rental each = *rentals;
        result += each.getFrequentRenterPoints();
    }
    return result;
}
這樣就完成「重構」這個動作了。
但是,這樣子的重構,讓程式碼變多,而且運算while的次數也變多了
這樣重構之後的效率不就變差了?

重構不需要擔心效率和程式碼多寡的問題,最佳化時,才需要擔心這一個部份。

現在,Customer內的任何程式碼都可以取用這些query method了。
如果沒有這些query methods,其它函式就必須了解Rental class,並自行建立回圈。(違反封裝)

接下來就可以放心的添加新功能了。

沒有留言:

張貼留言

(什麼是留言欄訊息?)