[ASP.NET]重構之路系列v7 –簡化判斷式

[ASP.NET]重構之路系列v7 –簡化判斷式

前言
前面提到了許多篇重構的方式,都是偏向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,就可以直接用來套上判斷式的意義。

 

 

 

 

 


blog 與課程更新內容,請前往新站位置:http://tdd.best/