病症:statement()这样一个长长的函数很明显需要进行重构。
要点:
代码块越小,代码的功能就越容易管理,代码的处理和移动也就越轻松。
步骤1:
找出代码的逻辑泥团switch语句,运用Extract Method将其提炼到独立函数中。首先在代码段里找出函数内的局部变量和参数,注意哪些被修改过(如:thisAmount),哪些没有被修改过(如:each)。任何不会被修改的变量都可以被当成参数传入新函数,如果只有一个变量会被修改,则可以将其当做返回值。
修改前的statement()函数:
string Customer::statement()
{
double totalAmount = 0;
int frequentRenterPoints = 0;
string result = "Rental Record for " + getName() + "\n";
vector<Rental>::iterator iterVec = _rentals.begin();
for (; iterVec != _rentals.end(); ++iterVec)
{
double thisAmout = 0;
Rental each = *iterVec;
// determine amounts for each line
switch(each.getMovie().getPriceCode())
{
//case Movie::REGULAR:
case 0:
thisAmout += 2;
if (each.getDaysRented() > 2)
{
thisAmout += (each.getDaysRented() - 2) * 1.5;
}
break;
//case Movie::NEW_RELEASE:
case 1:
thisAmout += each.getDaysRented() * 3;
break;
//case Movie::CHILDRENS:
case 2:
thisAmout += 1.5;
if (each.getDaysRented() > 3)
{
thisAmout += (each.getDaysRented() - 3) * 1.5;
}
break;
}
// add frequent renter points
++frequentRenterPoints;
// add bonus for a two day new release rental
if ((each.getMovie().getPriceCode() == Movie::NEW_RELEASE) && each.getDaysRented() > 1)
{
++frequentRenterPoints;
}
// show figures for this rental
result += "\t" + each.getMovie().getTitle() + "\t" + boost::lexical_cast<string>(thisAmout);
}
// add footer lines
result += "Amount owed is " + boost::lexical_cast<string>(totalAmount) + "\n";
result += "You earned " + boost::lexical_cast<string>(frequentRenterPoints) + "frequent renter points";
return result;
}
修改后的statement()函数以及amountFor函数:
string Customer::statement()
{
double totalAmount = 0;
int frequentRenterPoints = 0;
string result = "Rental Record for " + getName() + "\n";
vector<Rental>::iterator iterVec = _rentals.begin();
for (; iterVec != _rentals.end(); ++iterVec)
{
double thisAmout = 0;
Rental each = *iterVec;
thisAmout = amountFor(each);
// add frequent renter points
++frequentRenterPoints;
// add bonus for a two day new release rental
if ((each.getMovie().getPriceCode() == Movie::NEW_RELEASE) && each.getDaysRented() > 1)
{
++frequentRenterPoints;
}
// show figures for this rental
result += "\t" + each.getMovie().getTitle() + "\t" + boost::lexical_cast<string>(thisAmout);
}
// add footer lines
result += "Amount owed is " + boost::lexical_cast<string>(totalAmount) + "\n";
result += "You earned " + boost::lexical_cast<string>(frequentRenterPoints) + "frequent renter points";
return result;
}
int Customer::amountFor(Rental each)
{
int thisAmount = 0;
// determine amounts for each line
switch(each.getMovie().getPriceCode())
{
//case Movie::REGULAR:
case 0:
thisAmount += 2;
if (each.getDaysRented() > 2)
{
thisAmount += (each.getDaysRented() - 2) * 1.5;
}
break;
//case Movie::NEW_RELEASE:
case 1:
thisAmount += each.getDaysRented() * 3;
break;
//case Movie::CHILDRENS:
case 2:
thisAmount += 1.5;
if (each.getDaysRented() > 3)
{
thisAmount += (each.getDaysRented() - 3) * 1.5;
}
break;
}
return thisAmount;
}
步骤2:
修改变量名,利用visual Assist X 的 Refactor功能修改变量名相当方便。
要点:好的代码应该清楚表达出自己的功能,变量名称是代码清晰的关键。
另外,函数应该放在它所使用的数据的所属对象内,amountFor()应该移到Rental类中去。 运用Move Method:首先把代码复制到Rental类,调整代码并重新编译。 修改后的Rental.cpp:
#include "rental.h"
Rental::Rental(Movie movie, int daysRented)
{
_movie = movie;
_daysRented = daysRented;
}
int Rental::getDaysRented()
{
return _daysRented;
}
Movie Rental::getMovie()
{
return _movie;
}
double Rental::getCharge()
{
double result = 0;
// determine amounts for each line
switch(getMovie().getPriceCode())
{
//case Movie::REGULAR:
case 0:
result += 2;
if (getDaysRented() > 2)
{
result += (getDaysRented() - 2) * 1.5;
}
break;
//case Movie::NEW_RELEASE:
case 1:
result += getDaysRented() * 3;
break;
//case Movie::CHILDRENS:
case 2:
result += 1.5;
if (getDaysRented() > 3)
{
result += (getDaysRented() - 3) * 1.5;
}
break;
}
return result;
}
步骤3:
运用Replace Temp with Query把statement()中的thisAmount临时变量去掉:
修改后的statement():
string Customer::statement()
{
double totalAmount = 0;
int frequentRenterPoints = 0;
string result = "Rental Record for " + getName() + "\n";
vector<Rental>::iterator iterVec = _rentals.begin();
for (; iterVec != _rentals.end(); ++iterVec)
{
Rental each = *iterVec;
// add frequent renter points
++frequentRenterPoints;
// add bonus for a two day new release rental
if ((each.getMovie().getPriceCode() == Movie::NEW_RELEASE) && each.getDaysRented() > 1)
{
++frequentRenterPoints;
}
// show figures for this rental
result += "\t" + each.getMovie().getTitle() + "\t" + boost::lexical_cast<string>(each.getCharge());
totalAmount += each.getCharge();
}
// add footer lines
result += "Amount owed is " + boost::lexical_cast<string>(totalAmount) + "\n";
result += "You earned " + boost::lexical_cast<string>(frequentRenterPoints) + "frequent renter points";
return result;
}