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章 重构,第一个案例(3):运用多态取代switch 

//第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

相关文章: