重構手法之重新組織函數【1】



返回總目錄


本小節目錄:

1、Extract Method(提煉函數)

2、Inline Method(內聯函數)

3、Inline Temp(內聯臨時變量)


 

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...


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM