上一篇文章:java 代码重构-第一章(去除临时变量)
继续吧...
运用多态(Polymorphism)取代与价格相关的条件逻辑
前述有些重构码系从上版本里头拷贝过来——主要是循环设置部分。更深入的重构动作可以清除这些重复代码。我可以把处理表头(header)、表尾(footer)和报表细目的代码都分别提炼目出来。在 Form Template Method 实例中,.你可以看到如何做这些动作。但是,现在用户又开始嘀咕了,他们准备修改影片分类规则。我们尚未清楚他们想怎么做,但似乎新分类法很快就要引入,现有的分类法马上就要变更。与之相应的费用计算方式和常客积点计算方式都还待决定,现在就对程序做修改,肯定是愚蠢的。我必须进入费用计算和常客积点计算中,把「因条件 而异的代码」(译注:指的是switch 语句内的case 子句)替换掉,这样才能为 将来的改变镀上一层保护膜。现在,请重新戴回「重构」这顶帽子。
这个问题的第一部分是switch 语句。在另一个对象的属性(attribute)基础上运用switch 语句,并不是什么好主意。如果不得不使用,也应该在对象自己的数据上使用,而不是在别人的数据上使用。
看java代码
class Rental... double getCharge() { double result = 0; switch (getMovie().getPriceCode()) { case Movie.REGULAR: result += 2; if (getDaysRented() > 2) result += (getDaysRented() - 2) * 1.5; break; case Movie.NEW_RELEASE: result += getDaysRented() * 3; break; case Movie.CHILDRENS: result += 1.5; if (getDaysRented() > 3) result += (getDaysRented() - 3) * 1.5; break; } return result; }
这暗示getCharge() 应该移到Movie class 里头去:
class Movie... double getCharge(int daysRented) { double result = 0; switch (getPriceCode()) { case Movie.REGULAR: result += 2; if (daysRented > 2) result += (daysRented - 2) * 1.5; break; case Movie.NEW_RELEASE: result += daysRented * 3; break; case Movie.CHILDRENS: result += 1.5; if (daysRented > 3) result += (daysRented - 3) * 1.5; break; } return result; }
为了让它得以运作,我必须把「租期长度」作为参数传递进去。当然,「租期长度」来自收Rental 对象。计算费用时需要两份数据:「租期长度」和「影片类型」。为什么我选择「将租期长度传给Movie 对象」而不是「将影片类型传给Rental 对象」呢?因为本系统可能发生的变化是加入新影片类型,这种变化带有不稳定倾向。如果影片类型有所变化,我希望掀起最小的链滴,所以我选择在Movie 对象内计算费用。
我把上述计费方法放进Movie class 里头,然后修改Rental 的getCharge(),让它使用这个新函数(图1.12和图1.13):
class Rental... double getCharge() { return movie.getCharge(_daysRented); }
搬移getCharge() 之后,我以相同手法处理常客积点计算。这样我就把根据影片类型而变化的所有东西,都放到了影片类型所属的class 中。
以下是重构前的代码:
class Rental... int getFrequentRenterPoints() { if ((getMovie().getPriceCode() == Movie.NEW_RELEASE) && getDaysRented() > 1) return 2; else return 1; }
重构如下:
Class rental.. int getFrequentRenterPoints() { return movie.getFrequentRenterPoints(_daysRented); } class movie... int getFrequentRenterPoints(int daysRented) { if ((getPriceCode() == Movie.NEW_RELEASE) && daysRented > 1) return 2; else return 1; }