前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到教程。

上接  重构-改善既有代码的设计-第1例:租赁影片(1)

2  运用多态取代与价格相关的条件逻辑

2.1 最好不要在另一个对象的属性基础上运用switch语句,应该在对象自己的数据上使用。

2.1.1 移动 getCharge ,getFrequentRenterPoints 方法到Movie 类中去。把会根据影片类型的变化而变化的东西放在影片类中。

Movie 类改为:

package bean;/*** 影片* @author Administrator*/
public class Movie {public static final int CHILDRENS = 2;  // 儿童片public static final int REGULAR = 0; // 普通片public static final int NEW_RELEASE = 1; // 新片private String _title;private int _priceCode;public Movie(String _title, int _priceCode) {this._title = _title;this._priceCode = _priceCode;}public int getPriceCode() {return _priceCode;}public void setPriceCode(int _priceCode) {this._priceCode = _priceCode;}public String getTitle() {return _title;}/*** 计算租金* @param dayRented 租赁天数* @return*/double getCharge(int dayRented){double result = 0; // 租金// 确定每种片子的租金switch(getPriceCode()){case Movie.REGULAR:result += 2;if(dayRented > 2 ){result += (dayRented - 2) * 1.5;}break;case Movie.NEW_RELEASE:result += dayRented*3;break;case Movie.CHILDRENS:result += 1.5;if(dayRented > 3 ){result += (dayRented - 3) * 1.5;}break;}return result;}/*** 常客积分计算* @param dayRented 租赁天数* @return*/int getFrequentRenterPoints(int dayRented){// (新片+租赁时间达2天  积分+1 )if(getPriceCode() == Movie.NEW_RELEASE && dayRented > 1){return 2;}else{return 1;}}}

2.1.2 修改Rental 类中的  getCharge ,getFrequentRenterPoints方法,让它调用Movie 类提供的新函数。

Rental 类中的计算租金方法和常客积分计算方法 改为:

 /*** 常客积分计算* @return*/int getFrequentRenterPoints(){return _movie.getFrequentRenterPoints(_daysRented);}/*** 计算租金* @return*/double getCharge(){return _movie.getCharge(_daysRented);}

2.2  为了确保任何时候都要通过取值函数和赋值函数来访问 不愿意被外界直接访问的属性,我们用一个对赋值函数的调用来代替构造中的部分代码。

Movie 类的构造之前为:

 private String _title;private int _priceCode;public Movie(String _title, int _priceCode) {this._title = _title;this._priceCode = _priceCode;}public int getPriceCode() {return _priceCode;}public void setPriceCode(int _priceCode) {this._priceCode = _priceCode;}public String getTitle() {return _title;}

让构造不能直接访问 不愿意被外界直接访问的属性,构造现在改为:

public Movie(String _title, int _priceCode) {this._title = _title;setPriceCode(_priceCode);}

2.3 有新需求到来,有新品种影片,租金计算又有新算法 。

用到设计模式的状态模式State(对象行为型)。

于是新建一个Price抽象类,并在其内给2个抽象方法用于获取影片的计价类型和计算租金,积分计算。

再写多个子类继承Price并各自实现父类方法以实现对租金计算,积分计算的重构。

2.3.1 Price 及子类 :

package bean;/*** 租金* @author Administrator*/
public abstract class Price {abstract int getPriceCode();
}
package bean;/*** 儿童片租金* @author Administrator*/
public class ChildrensPrice extends Price {@Overrideint getPriceCode() {return Movie.CHILDRENS;}}
package bean;public class NewReleasePrice extends Price {@Overrideint getPriceCode() {return Movie.NEW_RELEASE;}}
package bean;/*** 普通片租金* @author Administrator*/
public class RegularPrice extends Price {@Overrideint getPriceCode() {return Movie.REGULAR;}}

2.3.2  修改Movie  类的计价类型属性为Price类型,并改写赋值函数 :

package bean;/*** 影片* @author Administrator*/
public class Movie {public static final int CHILDRENS = 2;  // 儿童片public static final int REGULAR = 0; // 普通片public static final int NEW_RELEASE = 1; // 新片private String _title;private Price _price; public Movie(String _title, int _priceCode) {this._title = _title;setPriceCode(_priceCode);}public int getPriceCode() {return _price.getPriceCode();}public void setPriceCode(int _priceCode) {switch(_priceCode){case REGULAR:_price = new RegularPrice();break;case NEW_RELEASE:_price = new NewReleasePrice();break;case CHILDRENS:_price = new ChildrensPrice();break;default:throw new IllegalArgumentException(" Incorrect PriceCode! ");}}    ...

2.3.4  把租金计算方法移动到 Price 类,在Movie 类中调用即可。

package bean;/*** 租金* @author Administrator*/
public abstract class Price {abstract int getPriceCode();/*** 计算租金* @param dayRented 租赁天数* @return*/double getCharge(int dayRented){double result = 0; // 租金// 确定每种片子的租金switch(getPriceCode()){case Movie.REGULAR:result += 2;if(dayRented > 2 ){result += (dayRented - 2) * 1.5;}break;case Movie.NEW_RELEASE:result += dayRented*3;break;case Movie.CHILDRENS:result += 1.5;if(dayRented > 3 ){result += (dayRented - 3) * 1.5;}break;}return result;}
}
 /*** 计算租金* @param dayRented 租赁天数* @return*/double getCharge(int dayRented){return _price.getCharge(dayRented); // 这是在 Movie 类中的调用}

2.3.5  重构租金计算方法,把每个getCharge 方法中switch 的每个 case 取出,在相应的Price子类中写一个覆盖函数。

最后把Price的 租金计算方法改为抽象方法。

租金类 Price 重构前:

package bean;/*** 租金* @author Administrator*/
public abstract class Price {abstract int getPriceCode();/*** 计算租金* @param dayRented 租赁天数* @return*/double getCharge(int dayRented){double result = 0; // 租金// 确定每种片子的租金switch(getPriceCode()){case Movie.REGULAR:result += 2;if(dayRented > 2 ){result += (dayRented - 2) * 1.5;}break;case Movie.NEW_RELEASE:result += dayRented*3;break;case Movie.CHILDRENS:result += 1.5;if(dayRented > 3 ){result += (dayRented - 3) * 1.5;}break;}return result;}
}

重构getCharge 方法后Price类 及子类 为:

package bean;/*** 租金* @author Administrator*/
public abstract class Price {abstract int getPriceCode();/*** 计算租金* @param dayRented 租赁天数* @return*/abstract double getCharge(int dayRented);}
package bean;/*** 儿童片租金* @author Administrator*/
public class ChildrensPrice extends Price {@Overrideint getPriceCode() {return Movie.CHILDRENS;}/*** 计算租金* @param dayRented 租赁天数* @return*/@Overridedouble getCharge(int dayRented){double result = 1.5;if(dayRented > 3){result += (dayRented - 3) * 1.5;}return result;}
}
package bean;
/*** 新片租金* @author Administrator*/
public class NewReleasePrice extends Price {@Overrideint getPriceCode() {return Movie.NEW_RELEASE;}/*** 计算租金* @param dayRented 租赁天数* @return*/@Overridedouble getCharge(int dayRented){return dayRented*3;}}
package bean;/*** 普通片租金* @author Administrator*/
public class RegularPrice extends Price {@Overrideint getPriceCode() {return Movie.REGULAR;}/*** 计算租金* @param dayRented 租赁天数* @return*/@Overridedouble getCharge(int dayRented){double result = 2;if(dayRented > 2){result += (dayRented - 2) * 1.5;}return result;}
}

2.3.6 对积分计算方法作相同重构。

从 Movie 类中移动积分计算方法到 Price 类中。Movie 类中调用Proce的积分计算方法就行了。

package bean;/*** 租金* @author Administrator*/
public abstract class Price {abstract int getPriceCode();/*** 计算租金* @param dayRented 租赁天数* @return*/abstract double getCharge(int dayRented);/*** 常客积分计算* @param dayRented 租赁天数* @return*/int getFrequentRenterPoints(int dayRented){// (新片+租赁时间达2天  积分+1 )if(getPriceCode() == Movie.NEW_RELEASE && dayRented > 1){return 2;}else{return 1;}}
}
package bean;/*** 影片* @author Administrator*/
public class Movie {public static final int CHILDRENS = 2;  // 儿童片public static final int REGULAR = 0; // 普通片public static final int NEW_RELEASE = 1; // 新片private String _title;private Price _price; public Movie(String _title, int _priceCode) {this._title = _title;setPriceCode(_priceCode);}public int getPriceCode() {return _price.getPriceCode();}.../*** 常客积分计算* @param dayRented 租赁天数* @return*/int getFrequentRenterPoints(int dayRented){return _price.getFrequentRenterPoints(dayRented);}}
 .../*** 常客积分计算* @param dayRented 租赁天数* @return*/int getFrequentRenterPoints(int dayRented){return _price.getFrequentRenterPoints(dayRented);}}

对 Proce 类的积分计算方法重构,只是为新片类型增加一个覆写函数,并在超类中保留原函数,使它成为一种默认行为。

package bean;/*** 租金* @author Administrator*/
public abstract class Price {abstract int getPriceCode();.../*** 常客积分计算* @param dayRented 租赁天数* @return*/int getFrequentRenterPoints(int dayRented){// 默认积1分return 1;}
}
package bean;
/*** 新片租金* @author Administrator*/
public class NewReleasePrice extends Price {.../*** 常客积分计算* @param dayRented 租赁天数* @return*/@Overrideint getFrequentRenterPoints(int dayRented){// (新片+租赁时间达2天  积分+1 )return (dayRented > 1) ? 2 : 1 ;}
}
 .../*** 常客积分计算* @param dayRented 租赁天数* @return*/@Overrideint getFrequentRenterPoints(int dayRented){// (新片+租赁时间达2天  积分+1 )return (dayRented > 1) ? 2 : 1 ;}
}

到此,重构-改善既有代码的设计-第1例:租赁影片,就重构完成了。
总结 :这样重构以后,不论是修改影片分类结构,还是修改租金计算规则又或积分计算规则就都容易多了 。

注:个人觉得 Movie 类中的 setPriceCode 方法  中得每种 price 的时候不该用构造函数,而是该直接调用各Price 子类 中的 getPriceCode 方法。

但此博文尊重原书中代码未作改动。

最后 所有类完整代码为:

package bean;import java.util.Enumeration;
import java.util.Vector;/*** 顾客* @author Administrator*/
public class  Customer{private String _name; // 顾客名字private Vector _rentals = new Vector();  // 租赁订单数组public Customer(String name) {super();this._name = name;}public void addRental(Rental arg){_rentals.addElement(arg);}public String getName() {return _name;}/*** 生成订单(打印凭条)* @return*/public String htmlStatement(){Enumeration rentals = _rentals.elements();String result = "<P><H1>Rentals for <EM> "+ getName() + "</EM></H1></P>\n";while( rentals.hasMoreElements()){Rental each = (Rental)rentals.nextElement();// 本次租赁记录说明result += each.getMovie().getTitle()+":"+ String.valueOf(each.getCharge())+"<BR>\n";}// 页脚result +="<P>You owe <EM>"+ String.valueOf(getTotalCharge())+"</EM></P>\n";result +="<P> on this rental you earned <EM> "+String.valueOf(getTotalFrequentRenterPoints())+"</EM> frequent renter points </P>";return result;}// 计算总积分private int getTotalFrequentRenterPoints(){int result = 0;Enumeration rentals = _rentals.elements();while(rentals.hasMoreElements()){Rental each = (Rental)rentals.nextElement();result += each.getFrequentRenterPoints();}return result;}// 计算总租金private double getTotalCharge(){double result = 0;Enumeration rentals = _rentals.elements();while(rentals.hasMoreElements()){Rental each = (Rental)rentals.nextElement();result += each.getCharge();}return result;}}
package bean;
/*** 租赁订单* @author Administrator*/
public class Rental {private Movie _movie ; // 影片private int _daysRented; // 租赁天数public Rental(Movie _movie, int _daysRented) {this._movie = _movie;this._daysRented = _daysRented;}public Movie getMovie() {return _movie;}public int getDaysRented() {return _daysRented;}/*** 常客积分计算* @return*/int getFrequentRenterPoints(){return _movie.getFrequentRenterPoints(_daysRented);}/*** 计算租金* @return*/double getCharge(){return _movie.getCharge(_daysRented);}}
package bean;/*** 影片* @author Administrator*/
public class Movie {public static final int CHILDRENS = 2;  // 儿童片public static final int REGULAR = 0; // 普通片public static final int NEW_RELEASE = 1; // 新片private String _title;private Price _price; public Movie(String _title, int _priceCode) {this._title = _title;setPriceCode(_priceCode);}public int getPriceCode() {return _price.getPriceCode();}public void setPriceCode(int _priceCode) {switch(_priceCode){case REGULAR:_price = new RegularPrice();break;case NEW_RELEASE:_price = new NewReleasePrice();break;case CHILDRENS:_price = new ChildrensPrice();break;default:throw new IllegalArgumentException(" Incorrect PriceCode! ");}}public String getTitle() {return _title;}/*** 计算租金* @param dayRented 租赁天数* @return*/double getCharge(int dayRented){return _price.getCharge(dayRented);}/*** 常客积分计算* @param dayRented 租赁天数* @return*/int getFrequentRenterPoints(int dayRented){return _price.getFrequentRenterPoints(dayRented);}}
package bean;/*** 租金+积分* @author Administrator*/
public abstract class Price {abstract int getPriceCode();/*** 计算租金* @param dayRented 租赁天数* @return*/abstract double getCharge(int dayRented);/*** 常客积分计算* @param dayRented 租赁天数* @return*/int getFrequentRenterPoints(int dayRented){// 默认积1分return 1;}
}
package bean;/*** 儿童片租金* @author Administrator*/
public class ChildrensPrice extends Price {@Overrideint getPriceCode() {return Movie.CHILDRENS;}/*** 计算租金* @param dayRented 租赁天数* @return*/@Overridedouble getCharge(int dayRented){double result = 1.5;if(dayRented > 3){result += (dayRented - 3) * 1.5;}return result;}
}
package bean;
/*** 新片租金* @author Administrator*/
public class NewReleasePrice extends Price {@Overrideint getPriceCode() {return Movie.NEW_RELEASE;}/*** 计算租金* @param dayRented 租赁天数* @return*/@Overridedouble getCharge(int dayRented){return dayRented*3;}/*** 常客积分计算* @param dayRented 租赁天数* @return*/@Overrideint getFrequentRenterPoints(int dayRented){// (新片+租赁时间达2天  积分+1 )return (dayRented > 1) ? 2 : 1 ;}
}
package bean;/*** 普通片租金* @author Administrator*/
public class RegularPrice extends Price {@Overrideint getPriceCode() {return Movie.REGULAR;}/*** 计算租金* @param dayRented 租赁天数* @return*/@Overridedouble getCharge(int dayRented){double result = 2;if(dayRented > 2){result += (dayRented - 2) * 1.5;}return result;}
}

《重构-改善既有代码的设计》-第1例:租赁影片(2)相关推荐

  1. 《重构-改善既有代码的设计》-第1例:租赁影片(1)

    前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家.点击跳转到教程. 买了<重构 - 改善既有代码的设计 >一书,一直没有好好看,大致过了下也觉得只是有点点印 ...

  2. 重构—改善既有代码的设计

    概述 1.1 参考资料 <重构-改善既有代码的设计>读后总结 <重构改善既有代码的设计> 22种代码的坏味道,一句话概括 1.2 何谓重构 首先要说明的是:视上下文不同,重构的 ...

  3. PHP 杂谈《重构-改善既有代码的设计》之二 对象之间搬移特性

    思维导图 索引: Ø Move Method(搬移函数) Ø Move Field (搬移值域) Ø Extract Class (提炼类) Ø Inline Class (将类内联化,就是把当前的类 ...

  4. 『重构--改善既有代码的设计』读书笔记----序

    作为C++的程序员,我从大学就开始不间断的看书,看到如今上班,也始终坚持每天多多少少阅读技术文章,书看的很多,但很难有一本书,能让我去反复的翻阅.但唯独『重构--改善既有代码的设计』这本书让我重复看了 ...

  5. PHP 杂谈《重构-改善既有代码的设计》之三 重新组织数据

    介绍 承接上文的PHP 杂谈<重构-改善既有代码的设计>之 重新组织你的函数继续重构方面的内容. 这章主要针对数据的重构. 1.争论的声音--直接访问Field还是通过函数(Accesso ...

  6. 重构 改善既有代码的设计:代码的坏

    以下内容来自<<重构 改善既有代码的设计>> 一.什么是重构 所谓重构(Refactoring)是这样一个过程:在不改变代码外在行为的前提下,对代码做出修改以改进程序的内部结构 ...

  7. 实践提高《重构改善既有代码的设计第2版》PDF中文+PDF英文+对比分析

    重构是编程的基础,是在不改变外部行为的前提下,有条不紊地改善代码.编程爱好者都知道,Martin Fowler 的<重构:改善既有代码的设计>已经成为全球有经验的程序员手中的利器,既可用来 ...

  8. 重构改善既有代码的设计(github源码)

    refactoring improving the design of existing code(重构改善既有代码的设计) https://github.com/CoderDream/refacto ...

  9. 重构:改善既有代码的设计(软件开发的不朽经典)

    重构:改善既有代码的设计(软件开发的不朽经典) 基本信息 作者: (美)Martin Fowler   译者: 熊节[同译者作品] 丛书名: 图灵程序设计丛书 出版社:人民邮电出版社 ISBN:978 ...

最新文章

  1. Node.js中的express框架,修改内容后自动更新(免重启),express热更新
  2. hdu1711 KMP模板
  3. JavaScript中的属性:如何遍历属性
  4. 20172329 2017-2018-2 《程序设计与数据结构》实验四报告
  5. 使用Lombok简化你的代码
  6. JVM虚拟机总结 内存分析及调试
  7. when is OData model initialized - finally found done by Framework
  8. 计算机软件侵权责任,对计算机软件侵权行为认定标准
  9. HDU 5842—— Lweb and String CCPC 网络赛 1011
  10. Mac Brew Uninstall MySql
  11. java jdom jar_jdom jar下载_jdom jar官方下载-太平洋下载中心
  12. VS Code 快捷打开(localhost)PHP页面
  13. 产品分析 淘宝、京东、平多多
  14. ggplot绘制小提琴图
  15. Hive体系结构介绍
  16. Z-Blog 添加收藏本站
  17. 7-1 用格里高利公式求给定精度的PI值 (15分)
  18. Caffe 代码解读之全连接层concat layer
  19. 【Python】计算机视觉 手掌图片穴位识别(二)
  20. 5款新手常用的java编程工具,有你正在用的吗?

热门文章

  1. python supervisor flask_supervisor配合uwsgi部署flask应用
  2. Qt-VS开发:解决VS中使用带有信号槽的导出对象库时,信号槽不工作的问题
  3. 【Python CheckiO 题解】Striped Words
  4. 【Python CheckiO 题解】House Password
  5. REVERSE-PRACTICE-BUUCTF-27
  6. 【你会用代码画年兽吗】20行代码使用JS实现虎年春节倒计时 —— 红红火火过虎年
  7. python中空格属于字符吗_举例说明python中空格是属于字符
  8. 【CodeForces - 144C】Anagram Search(尺取,滑窗问题,处理字符串计数)
  9. 动手学无人驾驶(2):车辆检测
  10. 图解算法学习笔记(一): 算法简介