[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判斷式,裡面要判斷的條件落落長,而且這種條件判斷,很常修改或新增其他新的需求。第一版的程式,可能只有寫『哥哥不在家,今天不賣酒』,隨著情況越來越多,程式經手越來越多人,最後程式就長這樣。
當我們看到這樣的程式,你可以選擇:
- 反正現在的程式活的好好的,不要去改他。前人都這樣加上去,我們就跟著這樣加上去。
- 一塊一塊的抽出來,沒有動到架構,我有100%信心把這一段程式寫的更人性化且不會衍生問題。
看完這篇文章,希望大家都可以勇敢的選2!
重構步驟
步驟一:
首先,我們先抽象地瞭解這個function要提供什麼功能。
- 根據條件來決定,賣不賣酒
-
條件有分成幾種類型:
- 根據『日期中的星期幾』來決定
- 根據『日期中的日子』來決定
- 根據『老闆的一堆毛』來決定
- 根據『客人的一堆毛』來決定
步驟二:
根據我們剛剛分析的第二點,一項一項把我們的條件拆開。
調整完的程式碼:
/// <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變數的命名,會讓判斷式看起來更容易瞭解意思。
步驟四:
最後,因為我們這個function也是要回傳bool,所以可以連最後一個if都直接拿掉。
這樣我們的function,跟重構前的程式比起來,有沒好讀很多? 不管那些條件是不是一堆or跟一堆and交錯,最後都可以將判斷式簡化成比較抽象地概念。
當抽象完成後,未來如果是還有其他條件加進來,我們可以看該條件是否可以歸在已經定好的分類,來決定要新增一個bool跟function,或直接新增在原有的function中。這樣一來,最高階的IsBusinessDay的function,就不容易因為條件細節而需要一直變動。當出現問題或需求異動時,我們也可以很快速的調整設計。
結論
雖然只是簡單的判斷式重構,但這一招真的是簡單好用,可以用很小的成本,馬上讓原本花撒撒的程式變乾淨。
另外要提醒的一點是,if的判斷式裡面,就不要再出現判斷某個bool變數、bool屬性或function回傳bool值是 == true或== false了,因為bool就代表了true/false,就可以直接用來套上判斷式的意義。
blog 與課程更新內容,請前往新站位置:http://tdd.best/