代码块越小,代码的功能越容易管理,代码的处理和移动也越简单。所以看到长长的代码块就要考虑是否可以把它”大卸八块”。第一步,找出”逻辑泥潭”。抽出逻辑块封装成一个方法,抽成方法的时候要注意变量的范围,被抽出方法外的被修改的变量应该要作为该方法的返回值返回。
比较之前的想法,欠缺以下的思考:
1.在A类中重构抽出的方法mA1后,没有再往下重构,应该继续思考:抽出的方法里的变量与该类的关联大吗?是否更加契合其他类(比如类B)呢?把抽出的方法mA1放到类B是否更适合呢?
2.代码量的减少不等于优化,不要总是因为恰好可共用一个循环,就把各自的逻辑都放进去(比如该方法的作用是循环输出”影片名”及其”信息”),但我会”顺便”把电影租赁”总价”和”总积分”都放到该循环里,计算完之后,取出变量,然后又各管各的进行其他逻辑。
举个栗子:
public String statement() {
Enumeration<Rental> rentals = _rentals.elements();
String result = "Rental Record for " + getName() + "\n";
while (rentals.hasMoreElements()) {
Rental each = (Rental) rentals.nextElement();
result += "\t" + each.getMovie().getTitle() + "\t" + each.getCharge() + "\n";
}
result += "Amount owed is " + getTotalCharge() + "\n";
result += "You earned " + getTotalFrequentRenterPoints() + " freequent renter points";
return result;
}
private double getTotalCharge() {
double result = 0;
Enumeration<Rental> rentals = _rentals.elements();
while (rentals.hasMoreElements()) {
Rental each = (Rental) rentals.nextElement();
result += each.getCharge();
}
return result;
}
private int getTotalFrequentRenterPoints() {
int result = 0;
Enumeration<Rental> rentals = _rentals.elements();
while (rentals.hasMoreElements()) {
Rental each = (Rental) rentals.nextElement();
result += each.getFrequentRenterPoints();
}
return result;
}
这样些逻辑确实会清晰,但如果遍历需要消耗时间大的话,还是会选择混写。
3.最重要的一点,面对多变需要扩展的业务,我们重构的目的就是要做最小、最安全、最方便的改动就能满足扩展的需求。这里用到了”states模式”,这也是我第一次接触该模式。对于设计模式还是不太了解,有些设计模式完全识别不出来。对于该模式,网上找来的总结很贴切:
1)状态模式的好处就是将于特定状态相关的行为局部化,并且将不同状态的行为分割开来。意即,将特定的状态相关的行为都放入一个对象中,由于所有与状态相关的代码都存在于某个ConcreteState中,所以通过定义新的子类可以很容易地增加新的状态和转换。(2)这样做的目的是为了消除庞大的条件分支语句,大的分支判断会使得它们难以修改和扩展,状态模式通过把各种状态转移逻辑分布到State的子类之间,来减少相互间的依赖。(3)当一个对象的行为取决于它们的状态,并且它必须在运行时刻根据状态改变它的行为时,就可以考虑使用状态模式。
放上我的例子:
package example;
/**
* @author jeff.zhan
* @version 2016年9月27日下午7:17:04
*
*/
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) {
_title = title;
setPriceCode(priceCode);
}
public int getPriceCode() {
return _price.getPriceCode();
}
public void setPriceCode(int arg) {
switch (arg) {
case REGULAR:
_price = new RegularPrice();
break;
case CHILDRENS:
_price = new ChildrensPrice();
break;
case NEW_RELEASE:
_price = new NewReleasePrice();
break;
default:
throw new IllegalArgumentException("Incorrect Price Code:" + arg);
}
}
public String getTitle() {
return _title;
}
public double getCharge(int daysRented) {
return _price.getCharge(daysRented);
}
public int getFrequentRenterPoints(int daysRented) {
return _price.getFrequentRenterPoints(daysRented);
}
}
package example;
/**
* @author jeff.zhan
* @version 2016年9月27日下午7:17:04
*
*/
public class NewReleasePrice extends Price{
@Override
int getPriceCode() {
// TODO Auto-generated method stub
return Movie.NEW_RELEASE;
}
@Override
double getCharge(int daysRented) {
// TODO Auto-generated method stub
return daysRented * 3;
}
@Override
public int getFrequentRenterPoints(int daysRented) {
// TODO Auto-generated method stub
return (daysRented > 1) ? 2 : 1;
}
}
package example;
/**
* @author jeff.zhan
* @version 2016年9月27日下午7:17:04
*
*/
public class RegularPrice extends Price {
@Override
int getPriceCode() {
// TODO Auto-generated method stub
return Movie.REGULAR;
}
@Override
double getCharge(int daysRented) {
// TODO Auto-generated method stub
double result = 2;
if (daysRented > 2) {
result += (daysRented - 2) * 1.5;
}
return result;
}
}
package example;
/**
* @author jeff.zhan
* @version 2016年9月27日下午7:17:04
*
*/
public class ChildrensPrice extends Price {
@Override
int getPriceCode() {
// TODO Auto-generated method stub
return Movie.CHILDRENS;
}
@Override
double getCharge(int daysRented) {
// TODO Auto-generated method stub
double result = 1.5;
if (daysRented > 3) {
result += (daysRented - 3) * 1.5;
}
return result;
}
}
总结:第3点尤为重要,这是我之前重构时候都没有考虑过得。这给了我在重构的时候多了一个新的思路。这也是代码水平好坏的很重要的一点的体现。另外关于重构,应该注重的我们当前各自的业务逻辑,如果是一个很小的无需扩展的业务逻辑,我觉得不用这么大费周章的做十分精细的重构。