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