推薦這個blog:

Award


(ASP.NET 2010、2011、2012年)

其他資源

簡體中文blog

最新回應

前言
前面提到了許多篇重構的方式,都是偏向pattern或較大面向的設計重構,在面對比較大的系統包袱時,或許大家比較沒法子運用的得心應手,所以接下來會穿插一些誰都可以進行重構的技巧,希望讓大家對改善系統能更有衝動。

重構通常針對的就是兩個東西:判斷式與迴圈。這一篇文章會提到,怎麼樣重構我們的判斷式,使其更容易閱讀,更具備未來修改的彈性。

範例說明
很無聊的在腦袋中,哼著『
戲鳳』這首歌,稍微調整了一下,就變成了我們重構的目標了。程式碼如下:

    /// <summary>
    /// idea from :戲鳳, http://www.youtube.com/watch?v=JRs_bqW39oA
    /// </summary>
    public class DrinkingStore
    {
        public Person Boss { get; set; }

        public bool IsBusinessDay(DateTime date, Person customer)
        {
            //一三五不賣酒
            if (date.DayOfWeek == DayOfWeek.Monday || date.DayOfWeek == DayOfWeek.Wednesday || date.DayOfWeek == DayOfWeek.Friday ||
                //初一十五不賣酒
                date.Day == 1 || date.Day == 15 ||
                //老闆不爽不賣酒, 老闆哥哥不在家不賣酒
                this.Boss.IsAngry || this.Boss.Brother == null ||
                //客人沒錢不賣酒//客人太醜不賣酒//客人太胖不賣酒
                customer.IsPoor || customer.IsUgly || customer.IsFat)
            {
                return false;
            }
            else
            {
                return true;
            }
        }
    }

可以看到程式中,一個if判斷式,裡面要判斷的條件落落長,而且這種條件判斷,很常修改或新增其他新的需求。第一版的程式,可能只有寫『哥哥不在家,今天不賣酒』,隨著情況越來越多,程式經手越來越多人,最後程式就長這樣。

當我們看到這樣的程式,你可以選擇:

  1. 反正現在的程式活的好好的,不要去改他。前人都這樣加上去,我們就跟著這樣加上去。
  2. 一塊一塊的抽出來,沒有動到架構,我有100%信心把這一段程式寫的更人性化且不會衍生問題。


看完這篇文章,希望大家都可以勇敢的選2!

重構步驟
步驟一:
首先,我們先抽象地瞭解這個function要提供什麼功能。

  1. 根據條件來決定,賣不賣酒
  2. 條件有分成幾種類型:
    • 根據『日期中的星期幾』來決定
    • 根據『日期中的日子』來決定
    • 根據『老闆的一堆毛』來決定
    • 根據『客人的一堆毛』來決定


步驟二:
根據我們剛剛分析的第二點,一項一項把我們的條件拆開。

image 

調整完的程式碼:

/// <summary>
    /// idea from :戲鳳, http://www.youtube.com/watch?v=JRs_bqW39oA
    /// </summary>
    public class DrinkingStore
    {
        public Person Boss { get; set; }

        public bool IsBusinessDay(DateTime date, Person customer)
        {
            if (WithoutSellingByDayOfWeek(date) ||
                WithoutSellingByDay(date) ||
                WithoutSellingByBoss() ||
                WithoutSellingByCustomer(customer))
            {
                return false;
            }
            else
            {
                return true;
            }
        }

        /// <summary>
        /// 一三五不賣酒
        /// </summary>
        /// <param name="date"></param>
        /// <returns></returns>
        private static bool WithoutSellingByDayOfWeek(DateTime date)
        {
            return date.DayOfWeek == DayOfWeek.Monday || date.DayOfWeek == DayOfWeek.Wednesday || date.DayOfWeek == DayOfWeek.Friday;
        }

        /// <summary>
        /// 初一十五不賣酒
        /// </summary>
        /// <param name="date"></param>
        /// <returns></returns>
        private static bool WithoutSellingByDay(DateTime date)
        {
            return date.Day == 1 || date.Day == 15;
        }

        /// <summary>
        /// 老闆不爽不賣酒, 老闆哥哥不在家不賣酒
        /// </summary>
        /// <returns></returns>
        private bool WithoutSellingByBoss()
        {
            return this.Boss.IsAngry || this.Boss.Brother == null;
        }

        /// <summary>
        /// 客人沒錢不賣酒
        /// 客人太醜不賣酒
        /// 客人太胖不賣酒
        /// </summary>
        /// <returns></returns>
        private bool WithoutSellingByCustomer(Person customer)
        {
            return customer.IsPoor || customer.IsUgly || customer.IsFat;
        }
    }

步驟三:
因為這個例子的判斷式裡面,因素有點多,為了可讀性,我會再宣告幾個變數來接各個情況回傳的bool值。透過bool變數的命名,會讓判斷式看起來更容易瞭解意思。

image

步驟四:
最後,因為我們這個function也是要回傳bool,所以可以連最後一個if都直接拿掉。

image

這樣我們的function,跟重構前的程式比起來,有沒好讀很多? 不管那些條件是不是一堆or跟一堆and交錯,最後都可以將判斷式簡化成比較抽象地概念。

當抽象完成後,未來如果是還有其他條件加進來,我們可以看該條件是否可以歸在已經定好的分類,來決定要新增一個bool跟function,或直接新增在原有的function中。這樣一來,最高階的IsBusinessDay的function,就不容易因為條件細節而需要一直變動。當出現問題或需求異動時,我們也可以很快速的調整設計。

結論
雖然只是簡單的判斷式重構,但這一招真的是簡單好用,可以用很小的成本,馬上讓原本花撒撒的程式變乾淨。

另外要提醒的一點是,if的判斷式裡面,就不要再出現判斷某個bool變數、bool屬性或function回傳bool值是 == true或== false了,因為bool就代表了true/false,就可以直接用來套上判斷式的意義。

 

 

 

 

 


點部落-In Joey

↑ Grab this Headline Animator


關連文章

[Memo]使用Trim(Char[])要注意的地方

[ASP.NET]重構之路系列v8 –合併重複的條件片段

[ASP.NET]重構之路系列v6 –抽象來看程式是否符合DRY原則

[Visual Studio]快速建立自己的project template

回應

  • # re: [ASP.NET]重構之路系列v7 –簡化判斷式 by 阿城

    另外一種想法..如果改直接 return 邏輯解讀上應該也滿清楚的..

    public bool IsBusinessDay(DateTime date, Person customer) {

    //一三五不賣酒
    if (date.DayOfWeek == DayOfWeek.Monday ) return false;
    if (date.DayOfWeek == DayOfWeek.Wednesday ) return false;
    if (date.DayOfWeek == DayOfWeek.Wednesday ) return false;

    //初一十五不賣酒
    if (date.Day == 1 ) return false;
    if (date.Day == 15 ) return false;

    //老闆不爽不賣酒, 老闆哥哥不在家不賣酒
    if (this.Boss.IsAngry ) return false;
    if (this.Boss.Brother == null ) return false;

    //客人沒錢不賣酒//客人太醜不賣酒//客人太胖不賣酒
    if (customer.IsPoor ) return false;
    if (customer.IsUgly ) return false;
    if (customer.IsFat ) return false;

    return true;

    }

    2011/7/27 下午 11:02 | 回覆

  • # re: [ASP.NET]重構之路系列v7 –簡化判斷式 by 91

    to 阿城 :
    您的方式沒有不好,我也提一下另一種角度的看法:

    如果程式碼複雜度是以可能的執行路徑數量來判定的話,這樣的function複雜度高很多。

    另外,只要任何一個需求異動,就得改到這個function,而且function可能越來越大。

    2011/7/27 下午 11:07 | 回覆

  • # re: [ASP.NET]重構之路系列v7 –簡化判斷式 by 91

    to 阿城 :
    建議您也可以看一下『重構-改善既有程式的設計』這本書裡面的第九章:簡化條件式,裡面的9.2 『合併條件式』,就剛好提到了 一樣的判斷式結果,不一樣的呈現方式,代表著不一樣的意義。

     

    2011/8/2 下午 08:32 | 回覆

登入後使用進階評論

Please add 8 and 6 and type the answer here: