重構CH1//重構第二步

  1. 重構CH1//重構第二步
    1. 提煉「常客積點計算」程式碼
    2. 去除暫時變數

重構CH1//重構第二步

原文連結: https://darkblack01.blogspot.com/2012/12/ch1.html
移植時的最後更新日期: 2012-12-01T11:11:04.583+08:00

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

承第一步的最後一段程式
在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,並自行建立回圈。(違反封裝)

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