重构手法之重新组织函数【1】
本小节目录:
1 Extract Method(提炼函数)
概要
你有一段代码可以被组织在一起并独立起来。
将这段代码放进一个独立函数中,并让函数名称解释该函数的作用。
动机
如果有一个过长的函数或者需要一段注释才能让人理解用途的代码,那么就将这段代码放进一个独立函数中。
简短而且命名良好的函数的好处:
- 函数粒度小,被复用的机会大
- 使高层函数读起来像注释
- 函数都是细粒度,更易被复写
还记得在第二种坏味道——Long Method中说的函数到底有多长吗?作者如是说:“在我看来,长度不是问题,关键在于函数名称和函数本体之间的语义距离。如果提炼可以强化代码的清晰度,那就去做,就算函数名称比提炼出来的代码还长也无所谓。”
范例
public class Receipt { private List<decimal> Discounts { get; set; } private List<decimal> ItemTotals { get; set; } public decimal CalculateGrandTotal() { decimal subTotal = 0m; //计算subTotal 的总和 foreach (decimal itemTotal in ItemTotals) { subTotal += itemTotal; } //subTotal 要循环减去discount,也就是计算Discount if (Discounts.Count > 0) { foreach (decimal discount in Discounts) { subTotal -= discount; } } //计算Tax decimal tax = subTotal * 0.065m; subTotal += tax; return subTotal; } }
重构后代码如下:
public class Receipt { private List<decimal> Discounts { get; set; } private List<decimal> ItemTotals { get; set; } public decimal CalculateGrandTotal() { decimal subTotal = CalculateSubTotal(); subTotal = CalculateDiscounts(subTotal); subTotal = CalculateTax(subTotal); return subTotal; } //计算subTotal 的总和 private decimal CalculateSubTotal() { decimal subTotal = 0m; foreach (decimal itemTotal in ItemTotals) { subTotal += itemTotal; } return subTotal; } //计算折扣 private decimal CalculateDiscounts(decimal subTotal) { if (Discounts.Count > 0) { foreach (decimal discount in Discounts) { subTotal -= discount; } } return subTotal; } //计算Tax private decimal CalculateTax(decimal subTotal) { decimal tax = subTotal * 0.065m; subTotal += tax; return subTotal; } }
小结
这个重构手法是不是很简单。我相信这个手法大多数人用的非常多,或许你潜意识里就是这么做的。
我不知道大家的公司有没有在代码编写规范里面把这个作为参考,比如一个方法最多不能超过多少行等等。这在一定程度上也能使程序员把这些复杂的逻辑剥离成意义很清楚的小方法。
2 Inline Method(内联函数)
概要
一个函数的本体与名称同样清楚易懂。
在函数调用点插入函数主体,然后移除该函数。
动机
使用间接层可以让函数通俗易懂,但是如果这个函数本体本就通俗易懂,那这个间接层就是无用的间接层,可以将其去除。
范例
int GetRating() { return MoreThanFiveLateDeliveries() ? 2 : 1; } bool MoreThanFiveLateDeliveries() { return _numberOfLateDeliveries > 5; }
对于这个函数来说,MoreThanFiveLateDeliveries这个方法就是一个无用的间接层。因为原函数本就很清晰,此处将其去除。
重构后代码如下:
int GetRating() { return _numberOfLateDeliveries > 5 ? 2 : 1; }
小结
这个手法是比较简单的。但是不要因为简单就不注意它。在内联函数的时候,要确定该函数不具有多态性,因为子类无法覆写一个根本不存在的函数。
3 Inline Temp(内联临时变量)
概要
你有一个临时变量,只被一个简单表达式赋值一次,而它妨碍了其他重构手法。
将所有对该变量的引用动作,替换为对它赋值的那个表达式自身。
动机
这个手法多半是作为下一个要讲的手法Replace Temp with Query的一部分来使用的。
单独使用的情况是:某个临时变量被赋予某个函数调用的返回值,而这个临时变量妨碍了其他重构手法。
范例
bool GetBasePrice() { double basePrice = anOrder.GetBasePrice(); return basePrice > 1000; }
重构后代码如下:
bool GetBasePrice() { return anOrder.GetBasePrice() > 1000; }
小结
在将临时变量替换为表达式自身的时候要注意,这个临时变量在该函数内是否只被赋值一次。如果不是一次,那么就不要这么做。
To Be Continued...