重新组织函数
提取函数
提取函数是将一个过于复杂的函数分解成多个小函数,然后在调用其他小函数即可。
// 书中的示例
内联函数
简而言之就是将函数中的内容替换掉,直接使用其中的代码,而不使用中间函数。
// 书中的示例
int getRating() {
return (moreThanFiveLateDeliveries()) ? 2 : 1;
}
boolean moreThanFiveLateDeliveries() {
return _numberOfLateDeliveries > 5; // _numberOfLateDeliveries 是一个成员变量,这里仅仅示例
}
// 改写后
int getRating() {
return (_numberOfLateDeliveries > 5) ? 2 : 1;
}
内联临时变量
与内联函数概念类似,只是这里处理的是临时变量。如果代码中出现一个临时变量,只被简单表达式赋值一次,而妨碍了其他重构手法的情况下,就可以去掉这个临时变量。
// 书中的示例
double basePrice = anOrder.basePrice();
return basePrice > 1000;
// 重构后
return anOrder.basePrice() > 1000;
以查询取代临时变量
代码中的临时变量替换成查询式从而更改为调用函数而不是判断临时变量,以达到去除临时变量的目的。作者在书中说:
临时变量的问题在于:它们是暂时的,而且只能在所属函数内使用。由于临时变量只在所属函数内可见,所以它们会驱使你写出更长的函数,因为只有这样你才能访问到需要的临时变量。如果把临时变量替换为一个查询,那么同一个类中的所有函数都可以获得这份信息。这将带给你极大帮助,使你能够为这个类编写更清晰的代码。
double basePrice = _quantity * _itemPrice;
if (basePrice > 1000) {
return basePrice * 0.95;
} else {
return basePrice * 0.98;
}
// 重构后
if (basePrice() > 1000) {
return basePrice() * 0.95;
} else {
return basePrice() * 0.98;
}
boule basePrice(){
return _quantity * _itemPrice;
}
引入解释性变量
某个表达式很复杂的情况下,使用临时变量来接收表达式的值。看到这里就会很疑惑,前面一直在主张我们尽量消除掉临时性变量,这里却又要引进临时性变量,那岂不是与前面的原则相悖吗?
这时就要从重构的目的上去理解了,而不是单单的重构手法。重构的目的是为了让代码可读性更好,可重复使用率更高,当某个表达式更复杂的时候,我们引入临时变量其实是一种提高可读性的手段,这也是符合重构的原则。
// 书中示例
if ((platform.toUpperCase().indexOf("MAC") > -1) &&
(browser.toUpperCase().indexOf("IE") > -1 &&
wasInitialized() &&
resize > 0))
{
// do something...
}
// 重构后
final boolean isMacOS = platform.toUpperCase().indexOf("MAC") > -1;
final boolean isIEBrowser = browser.toUpperCase().indexOf("IE") > -1;
final boolean wasResized = resize > 0;
if (isMacOS && isIEBrowser && wasInitialized() && wasResized){
// to something...
}
这里的重构手法就是将 3 个条件分别引入到 3 个对应的临时变量,不过也可以使用上面的重构手法 以查询取代临时变量
的手法来做。
// 我的重构,非书上示例,使用书上的代码
if (isMacOS() && isIEBrowser() && wasInitialized() && wasResized()){
// to something...
}
boolean isMacOS() {
return platform.toUpperCase().indexOf("MAC") > -1;
}
boolean isIEBrowser() {
return browser.toUpperCase().indexOf("IE") > -1;
}
boolean wasResized() {
return resize > 0;
}
分解临时变量
代码中有某个临时变量被赋值超过一次,它既不是循环变量,也不被用于收集计算结果。
针对每次赋值,创建一个独立、对应的临时变量。
// 书中的示例
double temp = 2 *(_height * _width);
System.out.println(temp);
temp = _height * _width;
System.out.println(temp);
// 重构后
double perimeter = 2 *(_height * _width);
System.out.println(perimeter);
double area = _height * _width;
System.out.println(area);
分解临时变量后,好处是显而易见的,上面的代码重构后就可以将 计算面积
和 计算周长
的方法分开抽取成函数。
移除对参数的赋值
函数中的参数对应着传入的值,如果直接修改参数列表中某个参数的值,添加一个临时变量来接收对应参数列表的参数值,然后修改这个临时变量。目的:不修改参数列表中参数的值,如果需要修改,添加一个新的临时变量。
// 书中的示例
int discount(int inputVal, int quantity, int yearToDate){
if (inputVal > 50) {
inputVal -= 2;
}
}
// 重构后
int discount(int inputVal, int quantity, int yearToDate){
int result = inputVal;
if (inputVal > 50) {
result -= 2;
}
}
知识点:
Java 中有按值传递和按引用传递两种类型,如果参数列表中的参数类型是对象类型,那么修改参数会影响到调用函数端的原先对象。
以函数对象取代函数
在一个大型函数中,对于其中的局部变量已经无法再使用抽取函数的手段了,此时可以尝试使用 以函数对象取代函数
重构模式。
将这个函数放进一个单独对象中,如此一来局部变量就成了对象内的字段。然后就可以在同一个对象中将这个大型函数分解为多个小型函数
// 书中的示例
class Account {
int gamma(int inputVal, int quantity, int yearToDate){
int importantValue1 = (inputVal * quantity) + delta();
int importantValue2 = (inputVal * yearToDate) + 100;
if ((yearToDate - importantValue1) > 100) {
importantValue2 -= 20;
}
int importantValue3 = importantValue2 * 7;
return importantValue3 -2 * importantValue1;
}
}
// 重构后
class Account {
int gamma(int inputVal, int quantity, int yearToDate){
return new Gamma(this, inputVal, quantity, yearToDate).compute();
}
}
class Gamma {
private final Account account;
private int inputVal;
private int quantity;
private int yearToDate;
private int importantValue1;
private int importantValue2;
private int importantValue3;
Gamma(Account account, int inputVal, int quantity, int yearToDate) {
this.account = account;
this.inputVal = inputVal;
this.yearToDate = yearToDate;
}
int comput() {
int importantValue1 = (inputVal * quantity) + delta();
int importantValue2 = (inputVal * yearToDate) + 100;
importantThing();
int importantValue3 = importantValue2 * 7;
return importantValue3 -2 * importantValue1;
}
void importantThing(){
if ((yearToDate - importantValue1) > 100) {
importantValue2 -= 20;
}
}
}
说明:
在平时编写代码的过程中, IDEA 有时候会提示出该种重构手段,这是我最讨厌的一种方式。因为,多增加了一个类,增加了一个类就意味着增加了复杂度。
替换算法
解决问题的方式有很多种,尽量使用其中最简单的那种方式来解决问题。
// 书中示例
String foundPerson(String[] people) {
for (int i = 0; i < people.length; i++) {
if (people[i].equals("Don")) {
return "Don";
}
if (peopl[i].equals("John")) {
return "John";
}
if (people[i].equals("Kent")) {
return "Kent";
}
}
return "";
}
// 重构后
String foundPerson(String[] people) {
List<String> candidates= Arrays.asList("Don", "John", "Kent");
for (int i = 0; i < people.length; i++) {
if (candidates.contains(people[i])) {
return people[i];
}
}
return "";
}
思考:
尽量去寻找解决问题最简单的那种方式,越简单出错的可能性也就越低。
网友评论