重构方法
临时变量只能在函数内使用,会使函数变得很长,所以尽可能少的使用临时变量。
一、内联临时变量
把对临时变量的引用,改成得到临时变量的表达式本身。
修改前:
private static Double calculateMoney(Integer num, Double price) {
return num * price;
}
public static void main(String[] args) {
Double moneySum = calculateMoney(10, 20.05);
if (moneySum > 100) {
System.out.println("买不起");
} else {
System.out.println("买得起");
}
}
修改后:
private static Double calculateMoney(Integer num, Double price) {
return num * price;
}
public static void main(String[] args) {
if (calculateMoney(10, 20.05) > 100) {
System.out.println("买不起");
} else {
System.out.println("买得起");
}
}
二、以查询取代临时变量
把临时变量的表达式提取到新函数,然后调用处直接用新函数就好~
修改前:
public static Double getPrice(Integer quantity, Integer itemPrice) {
int basePrice = quantity * itemPrice;
double discountFactor;
if (basePrice > 1000) {
discountFactor = 0.95;
} else {
discountFactor = 0.98;
}
return basePrice * discountFactor;
}
public static void main(String[] args) {
System.out.println("价格:"+getPrice(10,20));
}
修改后:
private static Integer getBasePrice(Integer quantity, Integer itemPrice) {
return quantity * itemPrice;
}
private static double getDiscountFactor(Integer quantity, Integer itemPrice) {
if (getBasePrice(quantity,itemPrice) > 1000) {
return 0.95;
} else {
return 0.98;
}
}
public static Double getPrice(Integer quantity, Integer itemPrice) {
return getBasePrice(quantity,itemPrice) * getDiscountFactor(quantity,itemPrice);
}
public static void main(String[] args) {
System.out.println("价格:"+getPrice(10,20));
}
三、引入解释性变量
将复杂表达式拆分,放入临时变量里,变量名称用来解释表达式用途。
// quantity:数量 itemPrice:单价
// 计算逻辑 价格 = 单价*数量 - 折扣 + 运费
// 折扣的计算逻辑:数量小于500,不打折,数量超过500的部分,打95折
// 运费的计算逻辑:单价*数量*0.1,但是超过100的,付100就好
private static double getPrice(Integer quantity, Integer itemPrice) {
return quantity * itemPrice -
Math.max(0, quantity - 500) * itemPrice * 0.05 +
Math.min(quantity * itemPrice * 0.1, 100);
}
public static void main(String[] args) {
System.out.println("价格:" + getPrice(10, 20));
}
可以看出,该函数虽然短,但是很难理解,这时候,可以把复杂的逻辑拆成临时变量,提高可读性。
修改后:
private static double getPrice(Integer quantity, double itemPrice) {
double basePrice = quantity * itemPrice;
double discount = Math.max(0, quantity - 500) * itemPrice * 0.05;
double shipping = Math.min(basePrice * 0.1, 100);
return basePrice - discount + shipping;
}
public static void main(String[] args) {
System.out.println("价格:" + getPrice(10, 20.05));
}
可以看出,产生了好多临时变量,继续优化如下:
private static double getBasePrice(Integer quantity, double itemPrice) {
return quantity * itemPrice;
}
private static double getDiscount(Integer quantity, double itemPrice) {
return Math.max(0, quantity - 500) * itemPrice * 0.05;
}
private static double getShipping(Integer quantity, double itemPrice) {
return Math.min(getBasePrice(quantity, itemPrice) * 0.1, 100);
}
private static double getPrice(Integer quantity, double itemPrice) {
return getBasePrice(quantity,itemPrice) - getDiscount(quantity,itemPrice) + getShipping(quantity,itemPrice);
}
public static void main(String[] args) {
System.out.println("价格:" + getPrice(10, 20.05));
}
四、分解临时变量
临时变量被赋值多次,且之间毫无关系(循环变量和结果收集变量是有关系的),那么就拆成多个不同的临时变量。
private static void getResult(Integer quantity, double itemPrice) {
double result = quantity * itemPrice;
if (result > 10) {
result = quantity / 2 * itemPrice;
if (result > 10) {
System.out.println("少买一半,还是买不起");
} else {
System.out.println("少买一半,终于买得起");
}
} else {
System.out.println("有钱 买得起");
}
}
public static void main(String[] args) {
getResult(2,6);
}
可以看出,想直接消除result变量,比较复杂(emmm,假装很复杂吧),两个result之间并没有什么关系,先把它拆开吧。
private static void getResult(Integer quantity, double itemPrice) {
double originalPrice = quantity * itemPrice;
if (originalPrice > 10) {
double laterPrice = quantity / 2 * itemPrice;
if (laterPrice > 10) {
System.out.println("少买一半,还是买不起");
} else {
System.out.println("少买一半,终于买得起");
}
} else {
System.out.println("有钱 买得起");
}
}
public static void main(String[] args) {
getResult(2,6);
}
接着对临时变量进行优化
private static double getOriginalPrice(Integer quantity, double itemPrice) {
return quantity * itemPrice;
}
private static double getLaterPrice(Integer quantity, double itemPrice) {
return quantity / 2 * itemPrice;
}
private static void getResult(Integer quantity, double itemPrice) {
if (getOriginalPrice(quantity, itemPrice) > 10) {
if (getLaterPrice(quantity, itemPrice) > 10) {
System.out.println("少买一半,还是买不起");
} else {
System.out.println("少买一半,终于买得起");
}
} else {
System.out.println("有钱 买得起");
}
}
public static void main(String[] args) {
getResult(2,6);
}
五、移除对参数的赋值
如果对参数进行修改重新赋值,是不合理的,尽量避免这种情况,因为JAVA内总是使用值传递的方式,方法体内的重新赋值并不会对调用方造成任何变化,如下:
public static void main(String[] args) {
Date d1 = new Date();
System.out.println(d1);
nextDate(d1);
System.out.println("(调用nextDate结果)date + 1 : " + d1);
Date d2 = new Date();
nextDate2(d2);
System.out.println("(调用nextDate2结果)date + 1 : " + d2);
}
private static void nextDate(Date date) {
date.setDate(date.getDate() + 1);
System.out.println("(nextDate)date + 1 : " + date);
}
private static void nextDate2(Date date) {
date = new Date(date.getYear(),date.getMonth(),date.getDate() + 1);
System.out.println("(nextDate2)date + 1 : " + date);
}
结果如下:
显然,对于参数date对象,对它进行修改是可以修改的(d1的前后值一致),
但是在调用函数里,对它重新赋值是不可以的,(d2前后值并不一致);
具体的修改方式如下:
修改前:
private int discount(int inputval, int quantity, int yearToDate) {
if (inputval > 50) inputval -= 2;
if (quantity > 100) inputval -= 1;
if (yearToDate > 10000) inputval -= 4;
return inputval;
}
修改后:
private int discount(int inputval, int quantity, int yearToDate) {
int result = inputval;
if (inputval > 50) result -= 2;
if (quantity > 100) result -= 1;
if (yearToDate > 10000) result -= 4;
return result;
}
参数只用来传递值,不要用来修改值啦~
六、以函数对象取代函数
如果有一个大型函数,里面的局部变量过多并且使用复杂,那么可以把这个函数放在一个单独的对象里,然后把局部变量变成对象内的字段,将这个大型函数分割成小型函数。
修改前:
public class Cook {
double deleta() {
return Math.random();
}
private double knownMethods(int inputval, int quantity, int yearToDate) {
double importantValue1 = (inputval * quantity) + deleta();
double importantValue2 = (inputval * quantity) + 100;
if ((yearToDate - importantValue1) > 100) {
importantValue2 -= 20;
}
double importantValue3 = importantValue2 * 7 ;
return importantValue3 - 2 * importantValue1;
}
}
由上,出现了很多临时对象,且对象之间存在一定的关系,如果要抽离,其实是蛮复杂的。
所以,直接抽离成一个对象,修改后如下:
public class Known {
private final Cook cook; // 源类
private int inputval; // 源方法参数1
private int quantity; // 源方法参数2
private int yearToDate; // 源方法参数3
private double importantValue1;
private double importantValue2;
private double importantValue3;
// 初始化方法,初始源类,以及源方法参数
Known(Cook cook, int inputval, int quantity, int yearToDate) {
this.cook = cook;
this.inputval = inputval;
this.quantity = quantity;
this.yearToDate = yearToDate;
}
double compute() {
importantValue1 = (inputval * quantity) + cook.deleta();
importantValue2 = (inputval * quantity) + 100;
importantThing();
importantValue3 = importantValue2 * 7 ;
return importantValue3 - 2 * importantValue1;
}
void importantThing() {
if ((yearToDate - importantValue1) > 100) {
importantValue2 -= 20;
}
}
}
public class Cook {
double deleta() {
return Math.random();
}
private double knownMethods(int inputval, int quantity, int yearToDate) {
return new Known(this,inputval,quantity,yearToDate).compute();
}
如上,把过多的临时变量都写在对象里,当作对象的变量,然后compute函数封装原来的逻辑,简化调用。
步骤:1、新建一个类,类名与其功能相同,变量包含:final 源类、源类方法参数、源类方法临时变量等;
2、类的初始化,将源类,和源类方法参数传递进去,初始化类,
3、创造compute函数,实现源方法逻辑;
4、调用处替换,替换成创造的新类以及调用方法;
七、替换算法
把某一种算法替换成更加清晰的算法
例如:
修改前:
String foundPerson(String[] people) {
for(int i = 0; i<people.length;i++) {
if(people[i].equals("Don")) {
return "Don";
}
if(people[i].equals("John")) {
return "John";
}
if(people[i].equals("kent")) {
return "kent";
}
}
return "";
}
修改后:
private static String foundPerson1(String[] people) {
List<String> candidates = Arrays.asList(new String[]{"anne", "John", "kent"});
for (int i = 0; i < people.length; ++i) {
if (candidates.contains(people[i])) {
return people[i];
}
}
return "";
}
寻找更合适,更简单的算法进行替换~