重构:第一个案例
2011-06-07 15:07
591 查看
在写重构的学习笔记之前,首先我们需要向伟大的软件设计师MartinFowler致敬,是他带给了我们重构的思想,敏捷的思想。
重构--改善既有代码的设计。意味着对现有运行中的代码进行新的修改、设计。这对很多项目经理来说是不可思议的,因为他们一直奉行的是软件业的一句经典“如果代码可以运行,就不要去修改它”在这条“真理”的引导下,当出现新的功能,新的BUG的时候,后续的程序员总是在原有的基础上修修补补,导致代码越来越庞大,业务逻辑越来越不明了,到最后维护的人员终于看不懂代码逻辑了,程序员开始抓狂了,白头发开始白了,职业病来了,项目死了。曾经在CSDN上流传着这样几个关于代码注释的笑话。1.//这段代码的实现逻辑,作为开发者的我已经不知道为什么这样设计了,请不要试图去理解这段代码并去修改它2.//如果你试图修改这段代码,但却导致了系统其他地方的BUG,请在下面的计数器上加一,以提醒下一位程序员不要动试图去修改它的念头。
什么时候我们的代码需要重构了?
我在看一本UI设计书《写给大家看的设计书》中提到,要学会设计其实很简单,主要是掌握3把斧!a.你需要知道哪里需要修改b.你需要知道该怎么样去修改c.实践、动手去修改它。我们学习并利用重构也是一样,首先你的知道代码中的坏味道,其实你的知道怎样去掉这些坏味道,最后动手去修改它。
首先我们通过一个简单的例子来给大家分享重构的过程和乐趣。题目是这样的:这是一个影片出租店德程序,计算每一位顾客的消费金额并打印详单。操作者告诉程序,顾客租了那些影片,租期多长,程序根据租期多长以及影片的类型算出费用。影片分为三类:普通片、儿童片、新片。除了计算费用外,还要为顾客计算积分,不同类型的积分不同。
先看一个不优秀的代码设计:依据题意,我们定义3个类Customer(顾客)Rental(租赁)Movice(电影)
Customer类
[code]
[/code]
朋友们,从上面的代码,你们找到了那些代码的坏味道了?
1.DuplicatedCode(重复代码):单我需要创建另外一种账单的打印方式:比如按照XML的格式打印时候,我需要另外写一个函数,然后重复前面获取租赁电影的价钱和积分。
2.LongMethod(过长的方法):Customer类的createBill功能不单一,方法过长
3.Customer类过多的魔鬼数字和字符,导致后续的字符和参数的替换不方便
4.SwitchStatements(Switch惊悚现身):Customer通过Switch来判断影片的类型,随着影片的类型增多,Switch的判断必然增多
5.发散式变化:单我的影片价格调整,积分调整的时候,我需要在Customer生成不同账单的函数中去修改。
6.依赖情节:这是一种“讲数据和对数据的操作行为包装在一起的技术”,有一种经典的气味是:函数对某个类的兴趣高过对自己所处的类的兴趣。
7.语法错误:代码语法的漏洞
如果你能发现以上的代码坏味道,甚至更多,那恭喜你,你已经开始进入了重构的大门。接下来我们通过重构来一步步优化代码。请记住:重构代码讲究一小步一小步的修改,测试。不要一开始就对整个结构进行调整,修改。
A.通过分析代码的坏味道,我们发现第3点:魔鬼数字是最好修改的。替换Customer类中的魔鬼数字得到新的Customer类为:红色部分是我们添加的常量定义,替换到魔鬼数字和字符
[code]privateStringname;
[/code]
B.过长的方法:我们发现Customer类的createBill()方法过长,通过分析该方法后,我们发现该方法主要做了以下几件事情:1.依次获得单个租赁碟片的价格2.依次获得单个碟片的积分3.按规程生成账单因此我们通过抽取业务逻辑形成方法的方式修改Customer类的createBill()方法,同时我们发现String使用的错误,当添加多个字符串的时候,需要使用StringBuffer。结果如下:红色部分为修改的代码
[code]privateStringname;
[/code]
C.依赖情节,我们发现getRentalPrice(),getRentalPoint()都和租赁有关,和顾客没有关系,因为我们需要把其移到对应的类中去,修改为Customer类以及Rental类为:
Rental类修改:
D.我们发现Rental类的getRentalPrice()跟影片的类型和影片的价格有关,因此其更应该放到Movice类里面去,修改Rental类和Movice类为。通过迁移,租赁价格的修改都集中到了Movice类中。
Movice类
D.到这里,我们发现惊悚的Switch类还没有处理掉。通过我们分析Switch主要是对不同的电影类型进行不同的处理,因此我们可以考虑抽取一个超级的电影类,不同的电影类型继承该类来解决Switch的问题。
通过我们一小步一小步的重构,让我们的程序更加优美,适应变化性更强。
重构--改善既有代码的设计。意味着对现有运行中的代码进行新的修改、设计。这对很多项目经理来说是不可思议的,因为他们一直奉行的是软件业的一句经典“如果代码可以运行,就不要去修改它”在这条“真理”的引导下,当出现新的功能,新的BUG的时候,后续的程序员总是在原有的基础上修修补补,导致代码越来越庞大,业务逻辑越来越不明了,到最后维护的人员终于看不懂代码逻辑了,程序员开始抓狂了,白头发开始白了,职业病来了,项目死了。曾经在CSDN上流传着这样几个关于代码注释的笑话。1.//这段代码的实现逻辑,作为开发者的我已经不知道为什么这样设计了,请不要试图去理解这段代码并去修改它2.//如果你试图修改这段代码,但却导致了系统其他地方的BUG,请在下面的计数器上加一,以提醒下一位程序员不要动试图去修改它的念头。
什么时候我们的代码需要重构了?
我在看一本UI设计书《写给大家看的设计书》中提到,要学会设计其实很简单,主要是掌握3把斧!a.你需要知道哪里需要修改b.你需要知道该怎么样去修改c.实践、动手去修改它。我们学习并利用重构也是一样,首先你的知道代码中的坏味道,其实你的知道怎样去掉这些坏味道,最后动手去修改它。
首先我们通过一个简单的例子来给大家分享重构的过程和乐趣。题目是这样的:这是一个影片出租店德程序,计算每一位顾客的消费金额并打印详单。操作者告诉程序,顾客租了那些影片,租期多长,程序根据租期多长以及影片的类型算出费用。影片分为三类:普通片、儿童片、新片。除了计算费用外,还要为顾客计算积分,不同类型的积分不同。
先看一个不优秀的代码设计:依据题意,我们定义3个类Customer(顾客)Rental(租赁)Movice(电影)
Customer类
publicclassCustomer{
[code]
/**顾客的姓名**/
privateStringname;
publicStringgetName(){
returnname;
}
/**租赁的所有影片和租期**/
privateListrentals=newArrayList();
publicCustomer(String_name){
name=_name;
}
/**添加租赁影片的租赁关系**/
publicvoidaddMovice(Rental_rental){
rentals.add(_rental);
}
/**生成账单**/
publicvoidcreateBill(){
doubletotalAmount=0;
intrenterPoint=0;
StringbillInfo="";
for(Rental_rental:rentals){
doublethisAmount=0;
intthisPoint=0;
inttype=_rental.getMovice().getType();
switch(type){
caseMovice.CHILDREN:
thisAmount+=2;
if(_rental.getDaysRental()>2){
thisAmount+=(_rental.getDaysRental()-2)*1.5;
}
break;
caseMovice.NEW:
thisAmount+=_rental.getDaysRental()*3;
break;
caseMovice.NORMAL:
thisAmount+=1.5;
if(_rental.getDaysRental()>3){
thisAmount+=(_rental.getDaysRental()-3)*1.5;
}
break;
default:
break;
}
thisPoint++;
if(type==Movice.NEW&&_rental.getDaysRental()>1){
thisPoint++;
}
totalAmount+=thisAmount;
renterPoint+=thisPoint;
billInfo+="书名:"+_rental.getMovice().getName()+"/t"+
"价格:"+thisAmount+"/t"+
"积分:"+thisPoint+"/t"+
"天数:"+_rental.getDaysRental()+"/n";
}
billInfo+="本次总价:"+totalAmount+"/n"+
"本次积分:"+renterPoint;
System.out.println(billInfo);
}
}
[/code]
Rental类
publicclassRental{
/**租赁的影片**/
privateMovicemovice;
/**影片的租期**/
privateintdaysRental;
publicRental(Movice_movice,int_daysRental){
movice=_movice;
daysRental=_daysRental;
}
publicMovicegetMovice(){
returnmovice;
}
publicintgetDaysRental(){
returndaysRental;
}
}
Movic类
publicclassMovice{
publicstaticfinalintNORMAL=0;
publicstaticfinalintCHILDREN=1;
publicstaticfinalintNEW=2;
/**影片的名称**/
privateStringname;
/**影片的类型**/
privateinttype;
publicStringgetName(){
returnname;
}
publicintgetType(){
returntype;
}
publicMovice(int_type,String_name){
name=_name;
type=_type;
}
}
朋友们,从上面的代码,你们找到了那些代码的坏味道了?
1.DuplicatedCode(重复代码):单我需要创建另外一种账单的打印方式:比如按照XML的格式打印时候,我需要另外写一个函数,然后重复前面获取租赁电影的价钱和积分。
2.LongMethod(过长的方法):Customer类的createBill功能不单一,方法过长
3.Customer类过多的魔鬼数字和字符,导致后续的字符和参数的替换不方便
4.SwitchStatements(Switch惊悚现身):Customer通过Switch来判断影片的类型,随着影片的类型增多,Switch的判断必然增多
5.发散式变化:单我的影片价格调整,积分调整的时候,我需要在Customer生成不同账单的函数中去修改。
6.依赖情节:这是一种“讲数据和对数据的操作行为包装在一起的技术”,有一种经典的气味是:函数对某个类的兴趣高过对自己所处的类的兴趣。
7.语法错误:代码语法的漏洞
如果你能发现以上的代码坏味道,甚至更多,那恭喜你,你已经开始进入了重构的大门。接下来我们通过重构来一步步优化代码。请记住:重构代码讲究一小步一小步的修改,测试。不要一开始就对整个结构进行调整,修改。
A.通过分析代码的坏味道,我们发现第3点:魔鬼数字是最好修改的。替换Customer类中的魔鬼数字得到新的Customer类为:红色部分是我们添加的常量定义,替换到魔鬼数字和字符
publicclassCustomer01{
[code]privateStringname;
privatestaticfinalStringBOOKNAME_STRING="书名:";
privatestaticfinalStringPRICE_STRING="价格:";
privatestaticfinalStringPOINT_STRING="积分:";
privatestaticfinalStringDAY_STRING="天数";
privatestaticfinalStringTOTLEAMOUNT_STRING="总价格:";
privatestaticfinalStringTOTLEPOINT_STRING="总积分";
privatestaticfinalStringCHAT_T_STRING="/t";
privatestaticfinalStringCHAT_N_STRING="/n";
privatestaticfinalintMOVICE_CHILDREN_PRICE=2;
/**儿童片租赁后可以使用的天数**/
privatestaticfinalintMOVICE_CHILDREN_DEADLINE=2;
/**超过租赁天数后,应付的价钱**/
privatestaticfinaldoubleMOVICE_CHILDREN_DELAY_PRICE=1.5;
privatestaticfinalintMOVICE_NEW_PRICE=3;
privatestaticfinaldoubleMOVICE_NORMAL_PRICE=1.5;
privatestaticfinalintMOVICE_NORMAL_DEADLINE=3;
privatestaticfinaldoubleMOVICE_NORMAL_DEALY_PRICE=1.5;
privatestaticfinalintPOINT_ADD_MIN_DAY=1;
publicStringgetName(){
returnname;
}
privateListrentals=newArrayList();
publicCustomer01(String_name){
name=_name;
}
publicvoidaddMovice(Rental_rental){
rentals.add(_rental);
}
publicvoidcreateBill(){
doubletotalAmount=0;
intrenterPoint=0;
StringBufferbillInfo=newStringBuffer();
for(Rental_rental:rentals){
doublethisAmount=0;
intthisPoint=0;
inttype=_rental.getMovice().getType();
switch(type){
caseMovice.CHILDREN:
thisAmount+=MOVICE_CHILDREN_PRICE;
if(_rental.getDaysRental()>MOVICE_CHILDREN_DEADLINE){
thisAmount+=(_rental.getDaysRental()-MOVICE_CHILDREN_DEADLINE)*MOVICE_CHILDREN_DELAY_PRICE;
}
break;
caseMovice.NEW:
thisAmount+=_rental.getDaysRental()*MOVICE_NEW_PRICE;
break;
caseMovice.NORMAL:
thisAmount+=MOVICE_NORMAL_PRICE;
if(_rental.getDaysRental()>MOVICE_NORMAL_DEADLINE){
thisAmount+=(_rental.getDaysRental()-MOVICE_NORMAL_DEADLINE)*MOVICE_NORMAL_DEALY_PRICE;
}
break;
default:
break;
}
thisPoint++;
if(type==Movice.NEW&&_rental.getDaysRental()>POINT_ADD_MIN_DAY){
thisPoint++;
}
totalAmount+=thisAmount;
renterPoint+=thisPoint;
billInfo.append(BOOKNAME_STRING+_rental.getMovice().getName()+CHAT_T_STRING);
billInfo.append(PRICE_STRING+thisAmount+CHAT_T_STRING);
billInfo.append(POINT_STRING+thisPoint+CHAT_T_STRING);
billInfo.append(DAY_STRING+_rental.getDaysRental()+CHAT_N_STRING);
}
billInfo.append(TOTLEAMOUNT_STRING+totalAmount+CHAT_N_STRING);
billInfo.append(TOTLEPOINT_STRING+renterPoint+CHAT_N_STRING);
System.out.println(billInfo);
}
}
[/code]
B.过长的方法:我们发现Customer类的createBill()方法过长,通过分析该方法后,我们发现该方法主要做了以下几件事情:1.依次获得单个租赁碟片的价格2.依次获得单个碟片的积分3.按规程生成账单因此我们通过抽取业务逻辑形成方法的方式修改Customer类的createBill()方法,同时我们发现String使用的错误,当添加多个字符串的时候,需要使用StringBuffer。结果如下:红色部分为修改的代码
publicclassCustomer02{
[code]privateStringname;
privatestaticfinalStringBOOKNAME_STRING="书名:";
privatestaticfinalStringPRICE_STRING="价格:";
privatestaticfinalStringPOINT_STRING="积分:";
privatestaticfinalStringDAY_STRING="天数";
privatestaticfinalStringTOTLEAMOUNT_STRING="总价格:";
privatestaticfinalStringTOTLEPOINT_STRING="总积分";
privatestaticfinalStringCHAT_T_STRING="/t";
privatestaticfinalStringCHAT_N_STRING="/n";
privatestaticfinalintMOVICE_CHILDREN_PRICE=2;
privatestaticfinalintMOVICE_CHILDREN_DEADLINE=2;
privatestaticfinaldoubleMOVICE_CHILDREN_DELAY_PRICE=1.5;
privatestaticfinalintMOVICE_NEW_PRICE=3;
privatestaticfinaldoubleMOVICE_NORMAL_PRICE=1.5;
privatestaticfinalintMOVICE_NORMAL_DEADLINE=3;
privatestaticfinaldoubleMOVICE_NORMAL_DEALY_PRICE=1.5;
privatestaticfinalintPOINT_ADD_MIN_DAY=1;
publicStringgetName(){
returnname;
}
privateListrentals=newArrayList();
publicCustomer02(String_name){
name=_name;
}
publicvoidaddMovice(Rental_rental){
rentals.add(_rental);
}
/**
*<获得用户租赁的碟片的价格和积分,生成账单>
*<1.获得单个租赁碟片的价格>
*<2.获得单个碟片的积分>
*<3.按规程生成账单>
*/
privateStringBufferbillInfo=newStringBuffer();
publicvoidcreateBill(){
doubletotalAmount=0;
intrenterPoint=0;
for(Rental_rental:rentals){
totalAmount+=getRentalPrice(_rental);
renterPoint+=getRentalPoint(_rental);;
createSingleBill(_rental);
}
addStatistics(totalAmount,renterPoint);
}
privatedoublegetRentalPrice(Rental_rental){
inttype=_rental.getMovice().getType();
doublethisAmount=0;
switch(type){
caseMovice.CHILDREN:
thisAmount+=MOVICE_CHILDREN_PRICE;
if(_rental.getDaysRental()>MOVICE_CHILDREN_DEADLINE){
thisAmount+=(_rental.getDaysRental()-MOVICE_CHILDREN_DEADLINE)*MOVICE_CHILDREN_DELAY_PRICE;
}
break;
caseMovice.NEW:
thisAmount+=_rental.getDaysRental()*MOVICE_NEW_PRICE;
break;
caseMovice.NORMAL:
thisAmount+=MOVICE_NORMAL_PRICE;
if(_rental.getDaysRental()>MOVICE_NORMAL_DEADLINE){
thisAmount+=(_rental.getDaysRental()-MOVICE_NORMAL_DEADLINE)*MOVICE_NORMAL_DEALY_PRICE;
}
break;
default:
break;
}
returnthisAmount;
}
privateintgetRentalPoint(Rental_rental){
intthisPoint=0;
thisPoint++;
if(_rental.getMovice().getType()==Movice.NEW&&_rental.getDaysRental()>POINT_ADD_MIN_DAY){
thisPoint++;
}
returnthisPoint;
}
privatevoidcreateSingleBill(Rental_rental){
billInfo.append(BOOKNAME_STRING+_rental.getMovice().getName()+CHAT_T_STRING);
billInfo.append(PRICE_STRING+getRentalPrice(_rental)+CHAT_T_STRING);
billInfo.append(POINT_STRING+getRentalPoint(_rental)+CHAT_T_STRING);
billInfo.append(DAY_STRING+_rental.getDaysRental()+CHAT_N_STRING);
}
privatevoidaddStatistics(doubletotalAmount,intrenterPoint){
billInfo.append(TOTLEAMOUNT_STRING+totalAmount+CHAT_N_STRING);
billInfo.append(TOTLEPOINT_STRING+renterPoint+CHAT_N_STRING);
System.out.println(billInfo);
}
}
[/code]
C.依赖情节,我们发现getRentalPrice(),getRentalPoint()都和租赁有关,和顾客没有关系,因为我们需要把其移到对应的类中去,修改为Customer类以及Rental类为:
publicclassCustomer03{
privateStringname;
privatestaticfinalStringBOOKNAME_STRING="书名:";
privatestaticfinalStringPRICE_STRING="价格:";
privatestaticfinalStringPOINT_STRING="积分:";
privatestaticfinalStringDAY_STRING="天数";
privatestaticfinalStringTOTLEAMOUNT_STRING="总价格:";
privatestaticfinalStringTOTLEPOINT_STRING="总积分";
privatestaticfinalStringCHAT_T_STRING="/t";
privatestaticfinalStringCHAT_N_STRING="/n";
publicStringgetName(){
returnname;
}
privateListrentals=newArrayList();
publicCustomer03(String_name){
name=_name;
}
publicvoidaddMovice(Rental03_rental03){
rentals.add(_rental03);
}
/**
*<获得用户租赁的碟片的价格和积分,生成账单>
*<1.获得单个租赁碟片的价格>
*<2.获得单个碟片的积分>
*<3.按规程生成账单>
*/
privateStringBufferbillInfo=newStringBuffer();
publicvoidcreateBill(){
doubletotalAmount=0;
intrenterPoint=0;
for(Rental03_rental:rentals){
totalAmount+=_rental.getRentalPrice();
renterPoint+=_rental.getRentalPoint();;
createSingleBill(_rental);
}
addStatistics(totalAmount,renterPoint);
}
privatevoidcreateSingleBill(Rental03_rental){
billInfo.append(BOOKNAME_STRING+_rental.getMovice().getName()+CHAT_T_STRING);
billInfo.append(PRICE_STRING+_rental.getRentalPrice()+CHAT_T_STRING);
billInfo.append(POINT_STRING+_rental.getRentalPoint()+CHAT_T_STRING);
billInfo.append(DAY_STRING+_rental.getDaysRental()+CHAT_N_STRING);
}
privatevoidaddStatistics(doubletotalAmount,intrenterPoint){
billInfo.append(TOTLEAMOUNT_STRING+totalAmount+CHAT_N_STRING);
billInfo.append(TOTLEPOINT_STRING+renterPoint+CHAT_N_STRING);
System.out.println(billInfo);
}
}
Rental类修改:
publicclassRental03{
privateMovicemovice;
privateintdaysRental;
privatestaticfinalintMOVICE_CHILDREN_PRICE=2;
privatestaticfinalintMOVICE_CHILDREN_DEADLINE=2;
privatestaticfinaldoubleMOVICE_CHILDREN_DELAY_PRICE=1.5;
privatestaticfinalintMOVICE_NEW_PRICE=3;
privatestaticfinaldoubleMOVICE_NORMAL_PRICE=1.5;
privatestaticfinalintMOVICE_NORMAL_DEADLINE=3;
privatestaticfinaldoubleMOVICE_NORMAL_DEALY_PRICE=1.5;
privatestaticfinalintPOINT_ADD_MIN_DAY=1;
publicRental03(Movice_movice,int_daysRental){
movice=_movice;
daysRental=_daysRental;
}
publicMovicegetMovice(){
returnmovice;
}
publicintgetDaysRental(){
returndaysRental;
}
publicdoublegetRentalPrice(){
inttype=getMovice().getType();
doublethisAmount=0;
switch(type){
caseMovice.CHILDREN:
thisAmount+=MOVICE_CHILDREN_PRICE;
if(getDaysRental()>MOVICE_CHILDREN_DEADLINE){
thisAmount+=(getDaysRental()-MOVICE_CHILDREN_DEADLINE)*MOVICE_CHILDREN_DELAY_PRICE;
}
break;
caseMovice.NEW:
thisAmount+=getDaysRental()*MOVICE_NEW_PRICE;
break;
caseMovice.NORMAL:
thisAmount+=MOVICE_NORMAL_PRICE;
if(getDaysRental()>MOVICE_NORMAL_DEADLINE){
thisAmount+=(getDaysRental()-MOVICE_NORMAL_DEADLINE)*MOVICE_NORMAL_DEALY_PRICE;
}
break;
default:
break;
}
returnthisAmount;
}
publicintgetRentalPoint(){
intthisPoint=0;
thisPoint++;
if(getMovice().getType()==Movice.NEW&&getDaysRental()>POINT_ADD_MIN_DAY){
thisPoint++;
}
returnthisPoint;
}
}
D.我们发现Rental类的getRentalPrice()跟影片的类型和影片的价格有关,因此其更应该放到Movice类里面去,修改Rental类和Movice类为。通过迁移,租赁价格的修改都集中到了Movice类中。
publicclassRental05{
privateMovice05movice;
privateintdaysRental;
privatestaticfinalintPOINT_ADD_MIN_DAY=1;
publicRental05(Movice05_movice,int_daysRental){
movice=_movice;
daysRental=_daysRental;
}
publicMovice05getMovice(){
returnmovice;
}
publicintgetDaysRental(){
returndaysRental;
}
publicdoublegetRentalPrice(){
returngetMovice().getTotalPrice(getDaysRental());
}
publicintgetRentalPoint(){
intthisPoint=0;
thisPoint++;
if(getMovice().getType()==Movice.NEW&&getDaysRental()>POINT_ADD_MIN_DAY){
thisPoint++;
}
returnthisPoint;
}
}
Movice类
publicclassMovice05{
publicstaticfinalintNORMAL=0;
publicstaticfinalintCHILDREN=1;
publicstaticfinalintNEW=2;
privatestaticfinalintMOVICE_CHILDREN_PRICE=2;
privatestaticfinalintMOVICE_NEW_PRICE=3;
privatestaticfinaldoubleMOVICE_NORMAL_PRICE=1.5;
privatestaticfinalintMOVICE_CHILDREN_DEADLINE=2;
privatestaticfinaldoubleMOVICE_CHILDREN_DELAY_PRICE=1.5;
privatestaticfinalintMOVICE_NORMAL_DEADLINE=3;
privatestaticfinaldoubleMOVICE_NORMAL_DEALY_PRICE=1.5;
privateStringname="";
privateinttype=0;
privateintthisAmount=0;
publicintgetTotalPrice(intdaysRental){
if(getType()==CHILDREN){
thisAmount+=MOVICE_CHILDREN_PRICE;
if(daysRental>MOVICE_CHILDREN_DEADLINE){
thisAmount+=(daysRental-MOVICE_CHILDREN_DEADLINE)*MOVICE_CHILDREN_DELAY_PRICE;
}
}elseif(getType()==NORMAL){
thisAmount+=MOVICE_NORMAL_PRICE;
if(daysRental>MOVICE_NORMAL_DEADLINE){
thisAmount+=(daysRental-MOVICE_NORMAL_DEADLINE)*MOVICE_NORMAL_DEALY_PRICE;
}
}elseif(getType()==NEW){
thisAmount+=MOVICE_NEW_PRICE*daysRental;
}
returnthisAmount;
}
publicStringgetName(){
returnname;
}
publicintgetType(){
returntype;
}
publicMovice05(int_type,String_name){
name=_name;
type=_type;
}
}
D.到这里,我们发现惊悚的Switch类还没有处理掉。通过我们分析Switch主要是对不同的电影类型进行不同的处理,因此我们可以考虑抽取一个超级的电影类,不同的电影类型继承该类来解决Switch的问题。
publicabstractclassMoviceSuper{
/**影片的价格**/
publicintprice;
/**影片的积分**/
publicintpoint;
/**一步影片可以租多少天**/
publicintrentalFreeDays;
/**超过租期了付的价钱**/
publicdoubledelayDayPrice;
/**电影的名称**/
publicStringname;
publicStringgetName(){
returnname;
}
publicMoviceSuper(String_name){
name=_name;
}
/**
*<获得租赁影片的价钱>
*<总价格=单个影片的价格+延迟时每天应付的价格>
*
*@paramdaysRental:租赁的天数
*@return
*/
publicabstractdoublegetRentalPrice(intdaysRental);
publicabstractintgetRentalPoint();
}
packagecom.chapter01;
publicclassMoviceChildextendsMoviceSuper{
privatestaticfinalintPOINT=1;
privatestaticfinalintPRICE=2;
privatestaticfinalintDEADLINE=2;
privatestaticfinaldoubleDELAY_PRICE=1.5;
publicMoviceChild(Stringname){
super(name);
//TODOAuto-generatedconstructorstub
price=PRICE;
rentalFreeDays=DEADLINE;
delayDayPrice=DELAY_PRICE;
point=POINT;
}
@Override
publicintgetRentalPoint(){
//TODOAuto-generatedmethodstub
returnpoint;
}
@Override
publicdoublegetRentalPrice(intdaysRental){
//TODOAuto-generatedmethodstub
doublethisAmount=0;
thisAmount+=price;
if(daysRental>rentalFreeDays){
thisAmount+=(daysRental-rentalFreeDays)*delayDayPrice;
}
returnthisAmount;
}
}
packagecom.chapter01;
publicclassMoviceNewextendsMoviceSuper{
privatestaticfinalintPRICE=3;
privatestaticfinalintPOINT=2;
publicMoviceNew(Stringname){
super(name);
price=PRICE;
delayDayPrice=PRICE;
rentalFreeDays=0;
point=POINT;
}
@Override
publicintgetRentalPoint(){
//TODOAuto-generatedmethodstub
returnpoint;
}
@Override
publicdoublegetRentalPrice(intdaysRental){
//TODOAuto-generatedmethodstub
returndaysRental*price;
}
}
packagecom.chapter01;
publicclassMoviceNormalextendsMoviceSuper{
privatestaticfinalintPRICE=2;
privatestaticfinalintDEADLINE=3;
privatestaticfinaldoubleDEALY_PRICE=1.5;
privatestaticfinalintPOINT=1;
publicMoviceNormal(Stringname){
super(name);
price=PRICE;
delayDayPrice=DEALY_PRICE;
rentalFreeDays=DEADLINE;
point=POINT;
}
@Override
publicintgetRentalPoint(){
//TODOAuto-generatedmethodstub
returnpoint;
}
@Override
publicdoublegetRentalPrice(intdaysRental){
doublethisAmount=0;
thisAmount+=price;
if(daysRental>price){
thisAmount+=(daysRental-price)*delayDayPrice;
}
returnthisAmount;
}
}
通过我们一小步一小步的重构,让我们的程序更加优美,适应变化性更强。
相关文章推荐
- 《重构-改善代码既有的设计》重构,第一个案例
- 重构,第一个案例(三)
- 第一章 重构,第一个案例
- 第1章 重构,第一个案例(3):运用多态取代switch
- 重构第一章 重构的第一个案例
- 重构,第一个案例(三)
- 第一章:重构,第一个案例(读书笔记系列1)
- 重构,第一个案例(三)
- 《重构改善既有代码的设计》第一个重构案例
- 重构,第一个案例(三)
- 第一章:重构,第一个案例(读书笔记系列2)
- 重构,第一个案例(三)
- 重构,第一个案例(三)
- 第一章 重构,第一个案例
- 重构-改善既有代码的设计——第一章 重构,第一个案例
- 重构,第一个案例(三)
- 第一章:重构,第一个案例(读书笔记系列3)
- 重构,第一个案例(三)
- 第1章 重构,第一个案例(2):分解并重组statement函数
- 重构 第1章 重构,第一个案例