表驅動方法

       上週接本上都浸泡在表驅動方法裏,重構了一塊多年的代碼,說是多年,其實也就是一年半左右的樣子:-)不過當我看到那些代碼的時候,震驚到了。代碼相當雜亂,整個功能模塊就是一個大的hard code,解決問題的方式十分的hack,這裏取hack的貶義~代碼最初是很簡單的,稍微hard一點的解決方案確實不是很刺眼,尤其對於應用來說,追求功能的發佈速度幾乎是第一要義,所以當初開發的時候也沒有注意設計,怎麼方便怎麼來。可是後來pm各種加feature,尤其是換了好幾個pm後,這塊的邏輯就已經抗不住各種折騰了。我之前嘗試過重構一次,不過當初才疏學淺(其實現在也沒好到哪去==!)基本上只是整理了一下代碼,沒能完成真正意義上的重構。這次由於要加入新的功能,實在是加不進去了。所以只能硬着頭皮重構。幸好,最近在開始閱讀《代碼大全》,按照導讀,正好一開是就看了表驅動方法,如獲至寶!又順帶閱讀了下《java編程思想》關於枚舉的一章。一時嘆息,當初怎麼沒有好好讀讀書啊!表驅動方法基本上解決了我長久對於複雜邏輯處理的困惑。這裏mark 一下。(例子均來源於《代碼大全》代碼可能有我自己修改在其中。)

       所謂的表驅動方法其實很簡單,就是構造一張表,然後從中查找出需要的結果。這個道理我也早就懂,可是從來沒有真正掌握真正奧義。比如說,我們想知道一年中的每個月的天數,不關心潤年的情況。如果是我,可能會不假思索就寫出下面的代碼

public int getDayNumber(int month) {
   switch (month) {
       case 1:
        return 31;
       case 2:
        return 28;
        
        ...
        
       case 12:
         return 31; 
   }
}

      裏面用到了switch語句已經朝整潔的代碼小步前進了一丟丟了。如果是再早一點的我,可能就直接上了12個if else語句了!當然switch 和if else也沒有太本質的區別,性能都是一樣的,只是switch看起來稍微好看一點而已。這裏我做了省略,因爲實在不想寫那麼多的重複代碼。然而如果用查找表的方式代碼就變成了下面的樣子

private static int[] DAYS_NUMBER = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
public int getDayNumber(int month) {
    return DAYS_NUMBER[month - 1];
}

      瞬間代碼高效簡潔了起來!當然,這裏應該有一些邊界判斷,但是核心的代碼就是這麼幾行,而且數組查找的效率明顯要比用12個if 判斷的高多了。當看到這一段的時候我已經被表驅動方法深深吸引住了。對代碼的可維護性和清晰度來說,都是質的飛躍!而且這還是最簡單的表驅動方法,直接查找表。還有索引訪問和階梯訪問,分別對應於複雜情況不同的邏輯查找方式。昨晚co的很晚,今天想早點睡,就不詳細寫其他兩種表驅動方法了~

     另一個想記錄的就是java 枚舉的用法。以前一直都是把枚舉僅僅用來當作常量的替代品。仔細看了《java編程思想》中關於枚舉的應用之後才發現,以前太naive了。首先,枚舉類型就可以作爲普通類來用的(當然不是說可以new 出來),可以有自己的公共方法,私有方法,域等等。而且可以定義抽象方法,然後在聲明各個枚舉的時候實現這些方法。EnumMap使用枚舉類型作爲鍵值,而且其性能和數組相同,因爲它本身的內部實現就是用數組實現的。這些特性放在一起讓枚舉類型成爲實現表驅動方法的非常好的途徑。在我的現實問題中,我就是定義了一個枚舉類型和一下抽象方法,這些抽象方法用來對應不同元素的行爲,另外定義了一個枚舉作爲不同元素的行爲觸發的條件判斷。然後把這兩個枚舉通過遍歷的方式放到一張EnumMap中實現了表的構造。在後續的應用中就只需要通過查找表就可以獲得想要的元素了。當然,實際的實現要比這個複雜一些,不過基本思路就是這樣。在我定義了表之後,我就可以非常方便的調整元素順序,或者修改相應的觸發條件或者行爲。代碼的可維護性上升很多。

      所以說經典就是經典,前輩們在長期的實踐中總結出的一套方法可以避免我們走很多彎路。需要滿懷敬畏的去學習前輩們的思想。


發佈了22 篇原創文章 · 獲贊 2 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章