重構, 第一個案例
Refactoring, a First Example
這是一個影片出租店用的程式,計算每位顧客的消費金額並列
印報表(statement)。操作者告訴程式:顧客租了哪些影片、租期多長,程式便
根據租賃時間和影片類型算出費用。影片分為三類:普通片、兒童片和新片。除
了計算費用,還要為常客計算點數;點數會隨著「租片種類是否為新片」而有不
同。
我以數個classes 表現這個例子?的元素。圖1.1 是一張UML class diagram(類別
圖),用以顯示這些classes。
// reactor_MovieRent.cpp : Defines the entry point for the console application.
//#include "stdafx.h"
#include "stdio.h"
#define snprintf _snprintf
/*
int main(int argc, char* argv[])
{
return 0;
}
*/
// movie-ref.cpp : Defines the entry point for the console application.
//
#include <string>
#include <vector>
#include <iostream>
using namespace std;
// Refactoring, a First Example, step7, (~p52)
enum TYPE
{
REGULAR,
NEW_RELEASE,
CHILDRENS
};
class Price
{
public:
virtual int getPriceCode () = 0;
virtual double getCharge (int daysRented) = 0;
int getFrequentRenterPoints (int daysRented)
{
return 1;
}
};
class ChildrensPrice:public Price
{
public:
int getPriceCode ()
{
return CHILDRENS;
}
double getCharge (int daysRented)
{
double result = 1.5;
if (daysRented > 3)
result += (daysRented - 3) * 1.5;
return result;
}
};
class NewReleasePrice:public Price
{
public:
int getPriceCode ()
{
return NEW_RELEASE;
}
double getCharge (int daysRented)
{
return daysRented * 3;
}
int getFrequentRenterPoints (int daysRented)
{
return (daysRented > 1) ? 2 : 1;
}
};
class RegularPrice:public Price
{
public:
int getPriceCode ()
{
return REGULAR;
}
double getCharge (int daysRented)
{
double result = 2;
if (daysRented > 2)
result += (daysRented - 2) * 1.5;
return result;
}
};
class Movie
{
private:
string _title; //movie name
Price *_price; //price code
public:
//default construtor for `Rental::Rental(Movie&, int)'
Movie ()
{
_title = "unname";
setPriceCode (0);
}
Movie (string title, int priceCode)
{
_title = title;
setPriceCode (priceCode);
}
Movie (const Movie& movie)
{
_title = movie._title;
setPriceCode (movie._price->getPriceCode());
}
~Movie()
{
delete _price;
}
int getPriceCode ()
{
return _price->getPriceCode ();
}
void setPriceCode (int arg)
{
switch (arg)
{
case REGULAR:
_price = new RegularPrice ();
break;
case CHILDRENS:
_price = new ChildrensPrice ();
break;
case NEW_RELEASE:
_price = new NewReleasePrice ();
break;
default:
cout << "Incorrect Price Code" << endl;
break;
}
}
string getTitle ()
{
return _title;
}
double getCharge (int daysRented)
{
return _price->getCharge (daysRented);
}
int getFrequentRenterPoints (int daysRented)
{
return _price->getFrequentRenterPoints (daysRented);
}
};
class Rental
{
private:
Movie _movie;
int _daysRented;
public:
Rental (Movie& movie, int daysRented)
{
_movie = movie;
_daysRented = daysRented;
}
int getDaysRented ()
{
return _daysRented;
}
Movie getMovie ()
{
return _movie;
}
double getCharge ()
{
return _movie.getCharge (_daysRented);
}
int getFrequentRenterPoints ()
{
return _movie.getFrequentRenterPoints (_daysRented);
}
};
typedef vector < Rental > Vector;
typedef vector < Rental >::iterator Reniter;
class Customer
{
private:
string _name;
Vector _rentals;
public:
Customer (string name)
{
_name = name;
}
void addRental (Rental& arg)
{
_rentals.push_back (arg);
}
string getName ()
{
return _name;
}
string statement ()
{
Reniter iter;
char amount[32];
string result =
string ("Rental Record for ") + getName () + string ("\n");
for (iter = _rentals.begin (); iter != _rentals.end (); ++iter)
{
Rental each = *iter;
// show figures for this rental
snprintf (amount, 32, "%f\n", each.getCharge ());
result += string ("\t") + each.getMovie ().getTitle ()
+ string ("\t") + string (amount);
}
// add footer lines
snprintf (amount, 32, "%f\n", getTotalCharge ());
result += string ("Amount owed is ") + string (amount);
snprintf (amount, 32, "%d", getTotalFrequentRenterPoints ());
result += string ("You earned ") + string (amount) +
string (" frequent renter points");
return result;
}
string htmlStatement ()
{
Reniter iter;
char amount[32];
string result =
string ("<H1>Rentals for <EM>") + getName () +
string ("</EM></H1><P>\n");
for (iter = _rentals.begin (); iter != _rentals.end (); ++iter)
{
Rental each = *iter;
// show figures for this rental
snprintf (amount, 32, "%f<BR>\n", each.getCharge ());
result += each.getMovie ().getTitle () + string (":") + string (amount);
}
// add footer lines?
snprintf (amount, 32, "%f</EM><P>\n", getTotalCharge ());
result += string ("<P>You owe <EM>") + string (amount);
snprintf (amount, 32, "%d</EM> frequent renter points<P>",
getTotalFrequentRenterPoints ());
result += string ("On this rental you earned <EM>") + string (amount);
return result;
}
int getTotalFrequentRenterPoints ()
{
int result = 0;
Reniter iter;
for (iter = _rentals.begin (); iter != _rentals.end (); ++iter)
{
Rental each = *iter;
result += each.getFrequentRenterPoints ();
}
return result;
}
double getTotalCharge ()
{
double result = 0;
Reniter iter;
for (iter = _rentals.begin (); iter != _rentals.end (); ++iter)
{
Rental each = *iter;
result += each.getCharge ();
}
return result;
}
};
int
main (int argc, char *argv[])
{
cout << "Refactoring, a First Example, step7" << endl;
Movie m1 = Movie ("Seven", NEW_RELEASE);
Movie m2 = Movie ("Terminator", REGULAR);
Movie m3 = Movie ("Star Trek", CHILDRENS);
Rental r1 = Rental (m1, 4);
Rental r2 = Rental (m1, 2);
Rental r3 = Rental (m3, 7);
Rental r4 = Rental (m2, 5);
Rental r5 = Rental (m3, 3);
Customer c1 = Customer ("jjhou");
c1.addRental (r1);
c1.addRental (r4);
Customer c2 = Customer ("gigix");
c2.addRental (r1);
c2.addRental (r3);
c2.addRental (r2);
Customer c3 = Customer ("jiangtao");
c3.addRental (r3);
c3.addRental (r5);
Customer c4 = Customer ("yeka");
c4.addRental (r2);
c4.addRental (r3);
c4.addRental (r5);
cout << c1.statement () << endl;
cout << c2.statement () << endl;
cout << c3.statement () << endl;
cout << c4.statement () << endl;
return 0;
}