重構CH1//未重構原始碼

重構是什麼?
《重構》的第一章表演了一段「什麼是重構?」
在這,把程式碼弄成C++,同義的表演一次。

程式規格:

  • 影片出租店用的程式
    計算每一位顧客的消費金額並列印報表(statment)
  • 操作者告訴程式    顧客租了哪些影片?
        租期多長?
    程式便計算出費用
  • 影片分為三類普通片、兒童片、新片
  • 除了計算費用,還要為常客計算點數依照「是否為新片」而所有不同

原本的程式碼

第一個類別
#include<string>

class Movie
{
        std::string _title;   //名稱
        int _priceCode;       //價格(代號)
    public:
        static int CHILDERNS = 2;
        static int REGULAR = 0;
        static int NEW_RELEASE = 1;

        Moive(String title, int priceCode);
        int  getPriceCode();
        void setPriceCode(int arg);
        String getTitle();
};

inline Movie::Moive(String title, int priceCode)
{
    _title = title;
    _priceCode = priceCode;
}

inline int Movie::getPriceCode()
{
    return  _priceCode;
}

inline String Movie::getTitle()
{
    return _title;
}

inline void Movie::setPriceCode()
{
    _priceCode = arg;
}


第二個類別
#include "CMovie.h"

class Rental
{
    Movie _movie;     //影片
    int _dayRented;   //租期

    public:
        Rental(Movie movie, int dayRented);
        int getDaysRented();
        Movie getMovie();
};

inline Rental::Rental(Movie movie, int dayRented)
{
    _movie = movie;
    _dayRented = daysRented;
}

inline int Rental::getDaysRented()
{
    return _dayRented;
}

inline Movie Rental::getMovie()
{
    return _movie;
}



第三個類別
#include <string>
#include <vector>
#include "CRental.h"

class Customer
{
    std::string _name;      //姓名
    std::vector _rentals;   //租借

    public:
        Customer(std::string name);
        void addRental(Rental arg);
        std::string getName();
        //接續下一頁
        std::string statement();
};

inline Customer::Customer(std::string name)
{
    _name = name;
}

inline void Customer::addRental(Rental arg)
{
    _rentals.push_back(arg);
}

inline std::string Customer::getName()
{
    return _name;
}

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())
    {
        double thisAmount = 0;
        Rental each = (Rental)rentals.next();  //取得一筆租借記錄

        //determine amounts for each line
        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;
        }

        //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(thisAmount) + "\n";
        totalAmount += thisAmount;
    }

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

    return result;
}


理性:
差的系統→很難找到修改點→很難修改
程式設計師很容易犯錯→引入bug

感性:
不好看,不美的程式

評價:
設計得不好,不符合物件導向的精神
Quick and dirty(快速而隨性)
Customer::statement()做的事,應該是由其它class完成的

接下來
使用者希望對系統做一些修改
希望以html的格式列印報表,直接在網頁上顯示,符合潮流

困難點
根本不可能reuse目前statement()的任何行為
唯一能做的就是做一個htmlStatement(),copy-past statement()的內容再修改
若再修改,不就??

接下來
使用者希望改變影片分類規則,但是還沒決定怎麼改
設想的方案中,都會影響顧客消費和常客積點的計算方式。


所以,我們需要開始重構(待續...)

沒有留言:

張貼留言

(什麼是留言欄訊息?)