iT邦幫忙

DAY 9
3

30天快速上手TDD系列 第 9

[Day 9]Refactoring legacy code簡介

到上一篇文章為止,TDD中所需具備的基本測試知識,已經告一段落。

接下來要練習的,是重構的手法。

接下來幾篇文章,會跟各位讀者朋友介紹:

  1. 要怎麼找到需要重構的部分
  2. 要怎麼讓程式碼會說話
  3. 要怎麼與測試結合

這一篇文章則會先介紹,要如何找到程式碼中需要重構的地方。

上一篇文章:[Day 8]Integration Testing & Web UI Testing
本系列文章專區
@現況
我們所面臨的系統狀況,通常也就是Legacy Code(提到Legacy Code,就要順便介紹一本好書:Working Effectively with Legacy Code),就像下圖一樣:

圖片來源:圖片來源:http://www.chancedia.com/?p=41470

就像廣告說的一樣:「每個Dev都喜歡乾淨的code,但是又喜歡把code弄髒。」臉紅

@重構的目的
我們希望可以把雜亂無章的code,乾淨整齊的放在它們所屬的位置上。

圖片來源:http://jung9572002.pixnet.net/blog/post/1733351-%E6%94%B6%E7%B4%8D%E9%81%94%E4%BA%BA

@重構的時機與目標
基本上最適合重構的時機有三類:

  1. Debug完成後

圖片來源:http://awards.gettyimages.com/awards.cfm?selCategory=all&display=photographer&workID=67&photographerID=7&photoID=70&sp\_sortID=1

  1. 需求異動

圖片來源:http://www.lykasal.com/2012/10/cats-that-pester-for-food-could-be.html

  1. 系統有Bad Smell的地方

圖片來源:http://www.thetorontopost.net/2012/09/smell-test-total-fail-for-rob-ford-in.html

簡單的說,就是要修改程式的時候,或是程式很髒的時候,適合重構。

但請記住:「一次只做一件事

@如何找出Bad Smell
這邊的範例,我建立了一個不同物流商會計算出不同運費的網站。

先以SourceMonitor為例,來找出系統中複雜度太高的function,並將它當做我們重構的目標。(SourceMonitor的介紹,有興趣的朋友可以看之前這篇文章:[Tool]SourceMonitor - 程式碼掃瞄

掃描後,按照Max Complexity排序,可以看到Prodcut_v0.aspx.cs,最大複雜度14,最大深度5。如下圖所示:

再點開詳細資訊後,可以看到 btnCalculate_Click這個方法,就是造成最大複雜度與最大深度的原因。如下圖所示:

[註]也可以使用VS2012/VS2010的程式碼度量,來找到複雜度過高的程式

接著,來看一下這個function的程式碼,如下所示:

protected void btnCalculate_Click(object sender, EventArgs e)
{
    if (this.IsValid)
    {
        if (this.drpCompany.SelectedValue == "1")
        {
            this.lblCompany.Text = "黑貓";
            var weight = Convert.ToDouble(this.txtProductWeight.Text);
            if (weight > 20)
            {
                this.lblCharge.Text = "500";
            }
            else
            {
                var fee = 100 + weight * 10;
                this.lblCharge.Text = fee.ToString();
            }
        }
        else if (this.drpCompany.SelectedValue == "2")
        {
            this.lblCompany.Text = "新竹貨運";
            var length = Convert.ToDouble(this.txtProductLength.Text);
            var width = Convert.ToDouble(this.txtProductWidth.Text);
            var height = Convert.ToDouble(this.txtProductHeight.Text);

            var size = length * width * height;

            //長 x 寬 x 高(公分)x 0.0000353
            if (length > 100 || width > 100 || height > 100)
            {
                this.lblCharge.Text = (size * 0.0000353 * 1100 + 500).ToString();
            }
            else
            {
                this.lblCharge.Text = (size * 0.0000353 * 1200).ToString();
            }
        }
        else if (this.drpCompany.SelectedValue == "3")
        {
            this.lblCompany.Text = "郵局";

            var weight = Convert.ToDouble(this.txtProductWeight.Text);
            var feeByWeight = 80 + weight * 10;

            var length = Convert.ToDouble(this.txtProductLength.Text);
            var width = Convert.ToDouble(this.txtProductWidth.Text);
            var height = Convert.ToDouble(this.txtProductHeight.Text);
            var size = length * width * height;
            var feeBySize = size * 0.0000353 * 1100;

            if (feeByWeight < feeBySize)
            {
                this.lblCharge.Text = feeByWeight.ToString();
            }
            else
            {
                this.lblCharge.Text = feeBySize.ToString();
            }
        }
        else
        {
            var js = "alert('發生不預期錯誤,請洽系統管理者');location.href='http://tw.yahoo.com/';";
            this.ClientScript.RegisterStartupScript(this.GetType(), "back", js, true);
        }
    }
}

上面就是一陀攤在角落的code,一眼望過去,每個字都認識,但卻要動腦袋猜測,甚至動手測試才能了解這一段code是什麼意思。除了難以理解以外,這樣巢狀if的設計方式,健壯性(robustness)上也相當薄弱。

呈現的畫面與功能,如下圖所示:

@小結
要重構之前,得先瞭解重構的目的、意義,以及如何找到需要重構的程式。

期望重構之後,能對原本可以正常執行的結果完全沒有影響,但程式碼因此具備了更高的可讀性、擴充性、健壯性等等...

重構的基本原則是:

  1. 建立測試,確保安全
  2. 由小到大,絕不貪心
  3. 適可而止,絕不偏執

由於重構在TDD中,也佔了很重要的一個角色,所以希望接下來幾篇,可以幫助讀者手把手的跟著練習一遍,這樣看似簡單、又像複雜、又沒啥彈性的程式碼,如何從亂七八糟,變成最後一應俱全的健壯程式。

@補充
有讀者朋友問到,什麼樣的程式碼算的上是Bad smell?

這邊列出筆者工作環境中的門檻值:

  1. 循環複雜度 > 10
  2. 繼承深度 > 3
  3. 區塊深度 > 4
  4. 相似程式碼 > 15行
  5. 綜合可維護性指數 < 75

以上,不代表超過標準就一定不好,但就像健康檢查報告的指數一樣,這些的確是需要被highlight出來說明的。

其他靜態程式碼分析的工具與投影片簡介,請參考這篇文章:[.NET][Tool]靜態程式碼分析工具簡介


上一篇
[Day 8]Integration Testing & Web UI Testing
下一篇
[Day 10]Refactoring 起手式 - 建立測試
系列文
30天快速上手TDD31

2 則留言

0
pajace2001
iT邦研究生 1 級 ‧ 2012-10-17 01:39:31

沙發
第一次認識H大大就是看到那亂亂的房間那張圖~~ 真懷念...

就是91 iT邦研究生 4 級 ‧ 2012-10-17 11:08:54 檢舉

XD 那我知道我們什麼時候認識的了。

請原諒我,因為這邊的發文介面比較無法像blog般自由跟順手,所以我覺得用投影片的方式來說明,應該可以讓讀者閱讀起來比較有趣跟舒服

pajace2001 iT邦研究生 1 級 ‧ 2012-10-17 13:13:32 檢舉

樓主你太客氣了啦~~
你的文章的真很讚~讚讚讚

0
ted99tw
iT邦高手 1 級 ‧ 2012-10-17 07:59:07

叫你加就加,叫你改就改...

哈哈哈哈哈哈

pajace2001 iT邦研究生 1 級 ‧ 2012-10-17 13:13:49 檢舉

那張圖真的很好笑哈哈哈哈哈哈哈哈

我要留言

立即登入留言