第1章 重構,第一個案例(3):運用多態取代switch


3. 運用多態取代與價格相關的條件邏輯

3.1 switch和“常客積分”代碼的再次搬遷

(1)switch:最好不要在另一個對象的屬性上運用switch語句

switch(getMovie().getPriceCode())   //在movie對象的priceCode屬性上運用switch
{                                   //這意味着可以將getCharge函數從Rental類移動到Movie類去
                                    //選擇在Movie類中封裝計算費用功能,還有一個
                                    //原因,就是可以控制因影片類型變化導致的計算
                                    //方式變化,從而對其它對象產生影響。
}

(2)常客積分:getFrequentRenterPoints函數的再次搬遷。用跟處理getCharge相同的手法處理常客積分,將因影片類型變化而變化的所有東西都放到Movie類中去處理。Rental類中只需調用Movie相應的方法即可。

【實例分析】影片出租1.3.1

 

//第1章:重構,第1個案例
//場景:影片出租,計算每一位顧客的消費金額
/*
說明:
1. 影片分3類:普通片、兒童片和新片。
2. 每種影片計算租金的方式。
   A.普通片:基本租金為2元,超過2天的部分每天加1.5元
   B.新片:租期*3
   C.兒童片:基本租金為1.5元,超過3天的部分每天加1.5元
3. 積分的計算:每借1片,積分加1,如果是新片且租期1天以上的額外贈送1分。
*/
#include <iostream>
#include <vector>
#include <string>
#include <sstream>
using namespace std;

//影片類
class Movie
{
private:
    string title; //片名
    int priceCode; //價格

public:
    enum MovieType{
        REGULAR = 0, //普通片
        NEW_RELEASE, //新片
        CHILDRENS    //兒童片
    };

    Movie(string title, int priceCode)
    {
        this->title = title;
        this->priceCode = priceCode;
    }

    string getTitle(){return title;}
    void setTitle(string value)
    {
        title = value;
    }

    int getPriceCode(){return priceCode;}
    void setPriceCode(int value)
    {
        this->priceCode = value;
    }

    //將原來Rental類中的getCharge移到該類中,並將租期作為參數傳入
    //搬到這里來的原因是
    //1.switch語句中getPriceCode為本類對象的屬性
    //2.封裝影片類型的變化導致計算方式變化於該類中,從而降低對其他類的影響
    double getCharge(int daysRented)
    {
        double result = 0 ;//相當於statement中的thisamount;

        switch(getPriceCode())
        {
        case Movie::REGULAR:
            result += 2;    //普通片基本租金為2元
            if(daysRented > 2)  //超過2天的每天加1.5元
                result +=(daysRented - 2 ) * 1.5;
            break;
        case Movie::NEW_RELEASE:
            result += daysRented * 3;    //新片的租金
            break;
        case Movie::CHILDRENS:
            result += 1.5;    //兒童片基本租金為1.5元
            if(daysRented > 3)  //超過3天的每天加1.5元
                result +=(daysRented - 3 ) * 1.5;
            break;
        }

       return result;
    }

    //將原Rental類中常客積分搬到該類中
    //原因是常客積分的計費方式與影片類型有關,也是為了控制當
    //影片類型變化時,由於計算方式變化對其他類的影響
    int getFrequentRenterPoints(int daysRented)
    {
        //如果是新片且租期超過1天以上,則額外送1分積分
        if ((getPriceCode() == Movie::NEW_RELEASE) &&
            daysRented > 1 )  return 2;
        else  return 1;
    }
};

//租賃類(表示某個顧客租了一部影片)
class Rental
{
private:
    Movie& movie;   //所租的影片
    int daysRented; //租期
public:
    Rental(Movie& movie, int daysRented):movie(movie)
    {
        this->daysRented = daysRented;
    }

    int getDaysRented(){return daysRented;}

    Movie& getMovie()
    {
        return movie;
    }

    double getCharge()
    {
        return movie.getCharge(daysRented);
    }

    //將原Customer類的statement中計算常客積分的代碼移到Rental類
    int getFrequentRenterPoints()
    {
        return movie.getFrequentRenterPoints(daysRented);
    }
};

//顧客類(用來表示顧客)
class Customer
{
private:
    string name; //顧客姓名
    vector<Rental*> rentals; //每個租賃記錄

     //獲得總消費
     double getTotalCharge()
     {
        double result = 0;
        vector<Rental*>::iterator iter = rentals.begin();
        while( iter != rentals.end())
        {
            Rental& each = *(*iter);

            result += each.getCharge();

            ++iter;
        }
        return result;
     }

     //獲得總積分
    int getTotalFrequentRenterPointers()
    {
        int result = 0;

        vector<Rental*>::iterator iter = rentals.begin();
        while( iter != rentals.end())
        {
            Rental& each = *(*iter);

            result += each.getFrequentRenterPoints();

            ++iter;
         }

         return result;
     }

     void cleanUp()
     {
         vector<Rental*>::iterator iter = rentals.begin();
         while( iter != rentals.end())
         {
             delete(*iter);
            ++iter;
         }
         rentals.clear();
     }

     template <typename T>
     string numToString(T num)
     {
         stringstream ss;
         ss << num;
         return ss.str();
     }

public:
    Customer(string name)
    {
        this->name = name;
    }

    void addRental(Rental* value)
    {
        rentals.push_back(value);
    }

    string getName(){return name;}

    //statement(報表),生成租賃的詳單
    string statement()
    {
        string ret = "Rental Record for " + name + "\n";

        vector<Rental*>::iterator iter = rentals.begin();
        while( iter != rentals.end())
        {
            Rental& each = *(*iter);

            //顯示每個租賃記錄

            ret += "\t" + each.getMovie().getTitle() + "\t" +
                 numToString(each.getCharge())+ "\n";

            ++iter;
        }

        //增加頁腳注釋
        ret += "Amount owed is " + numToString(getTotalCharge()) + "\n";//用getTotalCharge代替totalAmount
        ret += "You earned " + numToString(getTotalFrequentRenterPointers()) +"\n";

        return ret;
    }

    ~Customer()
    {
        cleanUp();
    }
};

void init(Customer& customer)
{
    Movie* mv = new Movie("倚天屠龍記",Movie::REGULAR);
    Rental* rt = new Rental(*mv, 2);
    customer.addRental(rt);

    mv = new Movie("新水滸傳",Movie::NEW_RELEASE);
    rt = new Rental(*mv, 3);
    customer.addRental(rt);

    mv = new Movie("喜羊羊與灰太狼",Movie::CHILDRENS);
    rt = new Rental(*mv, 5);
    customer.addRental(rt);
}

int main()
{
    Customer customer("SantaClaus");
    init(customer);

    cout << customer.statement() <<endl;

    return 0;
}
/*輸出結果
Rental Record for SantaClaus
        倚天屠龍記      2
        新水滸傳        9
        喜羊羊與灰太狼  4.5
Amount owed is 15.5
You earned 4
*/
View Code

3.2 運用子類取代類型碼

 

(1)使用繼承子類的方式,可以利用多態來取代switch語句

(2)重構的步驟

  ①使用“自我封裝字段”的方法將類型碼通過get/set函數封裝起來(如Movie類的getPriceCode函數)。如果類型碼被傳遞給構造函數,就需要將構造函數換成工廠方法(如createMovie函數)

  ②以類型碼的宿主類為基類,為類型碼的每一個數值建立一個相應的子類。在每個子類中覆寫類型碼的取值函數,使其返回相應的類型碼值。(見Movie子類getPriceCode)

  ③從父類中刪除保存類型碼的字段(即舊Movie類的priceCode字段),將類型碼訪問函數聲明為抽象函數(如Movie中的getPriceCode)

  ④使用pushDownMethod/Field方法將與特定類型碼相關的函數推到子類來實現(如本例中的getCharge函數)

(3)缺點:

  ①對於某個具體對象,在其生命周期中其狀態(或本例中類型碼)是不可改變(如代表Movie子類型的priceCode是不能更改的),所以當創建了一部影片出來以后,就不能修改其類型了。如,現在某影片是“新片”類型,但即使隨着時間的推移,也不能更改為“普通片”或“兒童片”了。

  ②我們總是在避免使用switch語句,雖然利用了多態將各個case語句的代碼分解到相應的子類中去實現。但在Movie類的createMovie函數中仍然要出現switch語句。幸運的是,僅此一處用到switch,並且只用於決定創建何種對象而沒有其他的業務邏輯,所以這樣的switch語句是可以接受的

【實例分析】影片出租1.3.2

//types.h

#include <vector>
#include <string>
#include <sstream>
using namespace std;

//影片類
class Movie
{
private:
    string title; //片名
    //int priceCode; //價格,注意,這里被注釋了

public:
    enum MovieType{
        REGULAR = 0, //普通片
        NEW_RELEASE, //新片
        CHILDRENS    //兒童片
    };

    Movie(string title);

    static Movie* createMovie(string title, int priceCode);

    string getTitle();
    void setTitle(string value);

    //類型碼的“自我封裝”(提供取值函數)
    virtual int getPriceCode() = 0;

    //將原來Rental類中的getCharge移到該類中,並將租期作為參數傳入
    //搬到這里來的原因是
    //1.switch語句中getPriceCode為本類對象的屬性
    //2.封裝影片類型的變化導致計算方式變化於該類中,從而降低對其他類的影響
    virtual double getCharge(int daysRented);

    int getFrequentRenterPoints(int daysRented);
};

//普通片:用子類取代類型碼
class RegularMovie: public Movie
{
public:
    RegularMovie(string title);
    int getPriceCode();
    double getCharge(int daysRented);
};

//兒童片:
class ChildrensMovie: public Movie
{

public:
    ChildrensMovie(string title);
    int getPriceCode();
    double getCharge(int daysRented);
};

//新片
class ReleaseMovie: public Movie
{
public:
    ReleaseMovie(string title);
    int getPriceCode();
    double getCharge(int daysRented);
};


//租賃類(表示某個顧客租了一部影片)
class Rental
{
private:
    Movie& movie;   //所租的影片
    int daysRented; //租期
public:
    Rental(Movie& movie, int daysRented);

    int getDaysRented();

    Movie& getMovie();

    double getCharge();

    //將原Customer類的statement中計算常客積分的代碼移到Rental類
    int getFrequentRenterPoints();
};

//顧客類(用來表示顧客)
class Customer
{
private:
    string name; //顧客姓名
    vector<Rental*> rentals; //每個租賃記錄

     //獲得總消費
     double getTotalCharge();

     //獲得總積分
    int getTotalFrequentRenterPointers();

     void cleanUp();

     template <typename T>
     string numToString(T num)
     {
        stringstream ss;
        ss << num;
        return ss.str();
    }

public:
    Customer(string name);

    void addRental(Rental* value);
    string getName();

    //statement(報表),生成租賃的詳單
    string statement();

    ~Customer();
};

//main.cpp

//第1章:重構,第1個案例
//場景:影片出租,計算每一位顧客的消費金額
/*
說明:
1. 影片分3類:普通片、兒童片和新片。
2. 每種影片計算租金的方式。
   A.普通片:基本租金為2元,超過2天的部分每天加1.5元
   B.新片:租期*3
   C.兒童片:基本租金為1.5元,超過3天的部分每天加1.5元
3. 積分的計算:每借1片,積分加1,如果是新片且租期1天以上的額外贈送1分。
*/
#include <iostream>
#include "types.h"
using namespace std;

//*********************************************影片類*************************************
Movie::Movie(string title)
{
    this->title = title;
}
//提供創建子類實例的靜態函數(也可以使用工廠方法)
Movie* Movie::createMovie(string title, int priceCode)
{
    Movie* ret = NULL;
    //利用子類替代switch的分支。我們總是在避免使用switch語句。但這里
    //只有一處用到switch,並且只用於決定創建何種對象而沒有其他的業務邏輯
    //所以這樣的switch語句是可以接受的。
    switch(priceCode)
    {
        case Movie::REGULAR:
            ret = new RegularMovie(title);
            break;
        case Movie::CHILDRENS:
            ret = new ChildrensMovie(title);
            break;
        case Movie::NEW_RELEASE:
            ret = new ReleaseMovie(title);
            break;
    }

    return ret;
}

string Movie::getTitle()
{
    return title;
}

void Movie::setTitle(string value)
{
    title = value;
}

double Movie::getCharge(int daysRented)
{
    return Movie::REGULAR;
}

//將原Rental類中常客積分搬到該類中
//原因是常客積分的計費方式與影片類型有關,也是為了控制當
//影片類型變化時,由於計算方式變化對其他類的影響
int Movie::getFrequentRenterPoints(int daysRented)
{
    //如果是新片且租期超過1天以上,則額外送1分積分
    if ((getPriceCode() == Movie::NEW_RELEASE) &&
        daysRented > 1 )  return 2;
    else  return 1;
}

//普通片:用子類取代類型碼
RegularMovie::RegularMovie(string title):Movie(title)
{
}

int RegularMovie::getPriceCode()
{
    return Movie::REGULAR;
}

//使用pushDownMethod(Field)方法將與特定類型碼相關的函數推到子類來實現
double RegularMovie::getCharge(int daysRented)
{
    double result = 2 ;

    if(daysRented > 2)  //超過2天的每天加1.5元
        result +=(daysRented - 2 ) * 1.5;

   return result;
}

//兒童片
ChildrensMovie::ChildrensMovie(string title):Movie(title)
{
}

int ChildrensMovie::getPriceCode()
{
    return Movie::CHILDRENS;
}

//使用pushDownMethod(Field)方法將與特定類型碼相關的函數推到子類來實現
double ChildrensMovie::getCharge(int daysRented)
{
    double result = 1.5;//兒童片基本租金為1.5元

    if(daysRented > 3)  //超過3天的每天加1.5元
        result +=(daysRented - 3 ) * 1.5;

    return result;
}

//新片
ReleaseMovie::ReleaseMovie(string title):Movie(title)
{
}

int ReleaseMovie::getPriceCode()
{
    return Movie::NEW_RELEASE;
}
//使用pushDownMethod(Field)方法將與特定類型碼相關的函數推到子類來實現
double ReleaseMovie::getCharge(int daysRented)
{
    return  daysRented * 3;    //新片的租金
}

//********************************租賃類(表示某個顧客租了一部影片)**********************************
Rental::Rental(Movie& movie, int daysRented):movie(movie)
{
    this->daysRented = daysRented;
}

int Rental::getDaysRented(){return daysRented;}

Movie& Rental::getMovie()
{
    return movie;
}

double Rental::getCharge()
{
    return movie.getCharge(daysRented);
}

//將原Customer類的statement中計算常客積分的代碼移到Rental類
int Rental::getFrequentRenterPoints()
{
    return movie.getFrequentRenterPoints(daysRented);
}

//*********************************顧客類(用來表示顧客)*************************************
//獲得總消費
double Customer::getTotalCharge()
{
    double result = 0;
    vector<Rental*>::iterator iter = rentals.begin();
    while( iter != rentals.end())
    {
        Rental& each = *(*iter);

        result += each.getCharge();

        ++iter;
    }
    return result;
}

 //獲得總積分
int Customer::getTotalFrequentRenterPointers()
{
    int result = 0;

    vector<Rental*>::iterator iter = rentals.begin();
    while( iter != rentals.end())
    {
        Rental& each = *(*iter);

        result += each.getFrequentRenterPoints();

        ++iter;
    }

    return result;
}

void Customer::cleanUp()
{
    vector<Rental*>::iterator iter = rentals.begin();
    while( iter != rentals.end())
    {
        delete(*iter);
        ++iter;
    }
    rentals.clear();
}

Customer::Customer(string name)
{
    this->name = name;
}

void Customer::addRental(Rental* value)
{
    rentals.push_back(value);
}

string Customer::getName(){return name;}

//statement(報表),生成租賃的詳單
string Customer::statement()
{
    string ret = "Rental Record for " + name + "\n";

    vector<Rental*>::iterator iter = rentals.begin();
    while( iter != rentals.end())
    {
        Rental& each = *(*iter);

        //顯示每個租賃記錄

        ret += "\t" + each.getMovie().getTitle() + "\t" +
             numToString(each.getCharge())+ "\n";

        ++iter;
    }

    //增加頁腳注釋
    ret += "Amount owed is " + numToString(getTotalCharge()) + "\n";//用getTotalCharge代替totalAmount
    ret += "You earned " + numToString(getTotalFrequentRenterPointers()) +"\n";

    return ret;
}

Customer::~Customer()
{
    cleanUp();
}

//************************************************初始化數據********************************************

void init(Customer& customer)
{
    Movie* mv = Movie::createMovie("倚天屠龍記",Movie::REGULAR);
    Rental* rt = new Rental(*mv, 2);
    customer.addRental(rt);

    mv = Movie::createMovie("新水滸傳",Movie::NEW_RELEASE);
    rt = new Rental(*mv, 3);
    customer.addRental(rt);

    mv = Movie::createMovie("喜羊羊與灰太狼",Movie::CHILDRENS);
    rt = new Rental(*mv, 5);
    customer.addRental(rt);
}

int main()
{
    Customer customer("SantaClaus");
    init(customer);

    cout << customer.statement() <<endl;

    return 0;
}
/*輸出結果
Rental Record for SantaClaus
        倚天屠龍記      2
        新水滸傳        9
        喜羊羊與灰太狼  4.5
Amount owed is 15.5
You earned 4
*/

3.3 以state/strategy取代類型碼

(1)對象的狀態在生命周期內可以變化,可以選用state模式(本例中有3種狀態:REGULAR、CHILDREN、NEW_RELEASE)。而前一個例子中,對象與其狀態是緊耦合的,本例利用state模式來組合對象與其狀態,達到松耦合的目的

(2)重構的步驟

  ①使用SelfEncapsulate Field來封裝類型碼,確保任何時候都通過取值和設值函數訪問類型代碼。

  ②新建一個Price類,並在其中提供類型相關的函數。如Price類中加入一個純虛函數getPriceCode,並在所有子類中加上對應的具體函數。

  ③為Price添加子類,每個子類對應一種類型碼。並提供getPriceCode的具體實現。

  ④利用pushDownMethod/Field方法將與類型碼相關的函數從Price類推到子類去實現。(如getCharge、getFrequentRenterPoints函數)

  ⑤在Movie類中保存一個Price的引用,用來保存新建的狀態對象。同時調整Movie中各個與類型碼相關的函數,將動作轉發到狀態對象上(如Movie的getCharge函數)。

(3)引入state模式的好處

  ①如果要修改任何與價格有關的行為只需修改相應的子類即可。添加新的定價標准也只需擴展新的Price子類即可。

  ②修改影片分類結構或是改變費用的計算規則改變常客積分計算規則都很容易。

【實例分析】影片出租1.3.3

 

//types.h

#include <vector>
#include <string>
#include <sstream>
using namespace std;

enum MovieType{
    REGULAR = 0, //普通片
    NEW_RELEASE, //新片
    CHILDRENS    //兒童片
};

//price類
class Price
{
public:
    virtual int getPriceCode() = 0;
    virtual double getCharge(int daysRented) = 0;
    virtual int getFrequentRenterPoints(int daysRented){return 1;}
    virtual ~Price(){}
};

//普通:
class RegularPrice: public Price
{
public:
    int getPriceCode();
    double getCharge(int daysRented);
};

//兒童片:
class ChildrensPrice: public Price
{
public:
    int getPriceCode();
    double getCharge(int daysRented);
};

//新片
class ReleasePrice: public Price
{
public:
    int getPriceCode();
    double getCharge(int daysRented);
    int getFrequentRenterPoints(int daysRented);
};

//影片類
class Movie
{
private:
    string title; //片名
    Price* price; //價格類

public:
    Movie(string title, int priceCode);
    ~Movie();

    string getTitle();
    void setTitle(string value);

    int getPriceCode();
    void setPriceCode(int priceCode);

    double getCharge(int daysRented);

    int getFrequentRenterPoints(int daysRented);
};

//租賃類(表示某個顧客租了一部影片)
class Rental
{
private:
    Movie& movie;   //所租的影片
    int daysRented; //租期
public:
    Rental(Movie& movie, int daysRented);

    int getDaysRented();

    Movie& getMovie();

    double getCharge();

    //將原Customer類的statement中計算常客積分的代碼移到Rental類
    int getFrequentRenterPoints();
};

//顧客類(用來表示顧客)
class Customer
{
private:
    string name; //顧客姓名
    vector<Rental*> rentals; //每個租賃記錄

     //獲得總消費
     double getTotalCharge();

     //獲得總積分
    int getTotalFrequentRenterPointers();

     void cleanUp();

     template <typename T>
     string numToString(T num)
     {
        stringstream ss;
        ss << num;
        return ss.str();
    }

public:
    Customer(string name);

    void addRental(Rental* value);
    string getName();

    //statement(報表),生成租賃的詳單
    string statement();

    ~Customer();
};

//main.cpp

//第1章:重構,第1個案例
//場景:影片出租,計算每一位顧客的消費金額
/*
說明:
1. 影片分3類:普通片、兒童片和新片。
2. 每種影片計算租金的方式。
   A.普通片:基本租金為2元,超過2天的部分每天加1.5元
   B.新片:租期*3
   C.兒童片:基本租金為1.5元,超過3天的部分每天加1.5元
3. 積分的計算:每借1片,積分加1,如果是新片且租期1天以上的額外贈送1分。
*/
#include <iostream>
#include "types.h"
using namespace std;

//*******************************************價格類**************************************
//普通片:用子類取代類型碼
int RegularPrice::getPriceCode()
{
    return REGULAR;
}

//使用pushDownMethod(Field)方法將與特定類型碼相關的函數推到子類來實現
double RegularPrice::getCharge(int daysRented)
{
    double result = 2 ;

    if(daysRented > 2)  //超過2天的每天加1.5元
        result +=(daysRented - 2 ) * 1.5;

   return result;
}

//兒童片
int ChildrensPrice::getPriceCode()
{
    return CHILDRENS;
}

//使用pushDownMethod(Field)方法將與特定類型碼相關的函數推到子類來實現
double ChildrensPrice::getCharge(int daysRented)
{
    double result = 1.5;//兒童片基本租金為1.5元

    if(daysRented > 3)  //超過3天的每天加1.5元
        result +=(daysRented - 3 ) * 1.5;

    return result;
}

//新片
int ReleasePrice::getPriceCode()
{
    return NEW_RELEASE;
}
//使用pushDownMethod(Field)方法將與特定類型碼相關的函數推到子類來實現
double ReleasePrice::getCharge(int daysRented)
{
    return  daysRented * 3;    //新片的租金
}

int ReleasePrice::getFrequentRenterPoints(int daysRented)
{
    return (daysRented > 1) ? 2: 1;
}

//*********************************************影片類*************************************
Movie::Movie(string title, int priceCode):price(NULL)
{
    this->title = title;
    setPriceCode(priceCode);
}

string Movie::getTitle()
{
    return title;
}

void Movie::setTitle(string value)
{
    title = value;
}

int Movie::getPriceCode()
{
    return price->getPriceCode();
}

void Movie::setPriceCode(int priceCode)
{
    if (price != NULL)
        delete price;

    switch(priceCode)
    {
    case REGULAR:
        price = new RegularPrice();
        break;
    case CHILDRENS:
        price = new ChildrensPrice();
        break;
    case NEW_RELEASE:
        price = new ReleasePrice();
        break;
    }
}

double Movie::getCharge(int daysRented)
{
    double ret = 0;
    if (price != NULL)
        ret = price->getCharge(daysRented);
    return ret;
}

int Movie::getFrequentRenterPoints(int daysRented)
{
    return price->getFrequentRenterPoints(daysRented);
}

Movie::~Movie()
{
    delete price;
}


//********************************租賃類(表示某個顧客租了一部影片)**********************************
Rental::Rental(Movie& movie, int daysRented):movie(movie)
{
    this->daysRented = daysRented;
}

int Rental::getDaysRented(){return daysRented;}

Movie& Rental::getMovie()
{
    return movie;
}

double Rental::getCharge()
{
    return movie.getCharge(daysRented);
}

//將原Customer類的statement中計算常客積分的代碼移到Rental類
int Rental::getFrequentRenterPoints()
{
    return movie.getFrequentRenterPoints(daysRented);
}

//*********************************顧客類(用來表示顧客)*************************************
//獲得總消費
double Customer::getTotalCharge()
{
    double result = 0;
    vector<Rental*>::iterator iter = rentals.begin();
    while( iter != rentals.end())
    {
        Rental& each = *(*iter);

        result += each.getCharge();

        ++iter;
    }
    return result;
}

 //獲得總積分
int Customer::getTotalFrequentRenterPointers()
{
    int result = 0;

    vector<Rental*>::iterator iter = rentals.begin();
    while( iter != rentals.end())
    {
        Rental& each = *(*iter);

        result += each.getFrequentRenterPoints();

        ++iter;
    }

    return result;
}

void Customer::cleanUp()
{
    vector<Rental*>::iterator iter = rentals.begin();
    while( iter != rentals.end())
    {
        delete(*iter);
        ++iter;
    }
    rentals.clear();
}

Customer::Customer(string name)
{
    this->name = name;
}

void Customer::addRental(Rental* value)
{
    rentals.push_back(value);
}

string Customer::getName(){return name;}

//statement(報表),生成租賃的詳單
string Customer::statement()
{
    string ret = "Rental Record for " + name + "\n";

    vector<Rental*>::iterator iter = rentals.begin();
    while( iter != rentals.end())
    {
        Rental& each = *(*iter);

        //顯示每個租賃記錄

        ret += "\t" + each.getMovie().getTitle() + "\t" +
             numToString(each.getCharge())+ "\n";

        ++iter;
    }

    //增加頁腳注釋
    ret += "Amount owed is " + numToString(getTotalCharge()) + "\n";//用getTotalCharge代替totalAmount
    ret += "You earned " + numToString(getTotalFrequentRenterPointers()) +"\n";

    return ret;
}

Customer::~Customer()
{
    cleanUp();
}

//************************************************初始化數據********************************************

void init(Customer& customer)
{
    Movie* mv = new Movie("倚天屠龍記", REGULAR);

    mv->setPriceCode(NEW_RELEASE); //重新改變影片的類型為NEW_RELEASE
                                   //這在上個例子是不可能的!

    Rental* rt = new Rental(*mv, 2);
    customer.addRental(rt);

    mv = new Movie("新水滸傳", NEW_RELEASE);
    rt = new Rental(*mv, 3);
    customer.addRental(rt);

    mv = new Movie("喜羊羊與灰太狼", CHILDRENS);
    rt = new Rental(*mv, 5);
    customer.addRental(rt);
}

int main()
{
    Customer customer("SantaClaus");
    init(customer);

    cout << customer.statement() <<endl;

    return 0;
}
/*輸出結果
Rental Record for SantaClaus
        倚天屠龍記      6
        新水滸傳        9
        喜羊羊與灰太狼  4.5
Amount owed is 19.5
You earned 5
*/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM