推薦這個blog:

Award


(ASP.NET 2010、2011、2012年)

其他資源

簡體中文blog

最新回應

前言
好久沒PO文了,深潛了一段時間,努力的吸取大型系統經驗跟學習更底子的知識,雖然累但也是感覺收穫滿滿。最近報名了iT邦幫忙的第四屆鐵人鍛鍊賽,打算把之前一些跟系統品質、重構、測試等相關文章整理一系列,希望屆時也可以比較有條理的整理在wiki上。

回到主題,今天要分享的部分,是把一陀很類似卻又攤在程式碼中的判斷式,想辦法抽象化。例子會有點像
[ASP.NET]重構之路系列v7 –簡化判斷式這一篇,但相信這次舉的例子會更貼近大家在系統上碰到的需求。

需求說明

  1. 有多筆會員資料要經過多種格式的驗證
  2. 最後需得到驗證的結果,以及驗證錯誤的訊息


直覺設計出來的原型程式就會長的像下面一樣:

原本的code

只是將原本的Validation,簡單的做個殼而已。 

原本的Validation


這邊的程式,至少已經將每個驗證的邏輯抽到了Validation的Class中,將可能變化的驗證邏輯封裝起來。我得強調一下,這樣的程式並沒有『錯』,但這邊提到的是已知未來一定會增加新的驗證邏輯,如何設計面對這樣的需求,不會動到主程式商業邏輯,就是這篇的目的。

重構步驟
步驟一:
首先把我們的眼睛瞇起來一點,(或是你要打開萬花筒寫輪眼、白眼、輪迴眼、血紅眼都可以),用抽象化的方式來看原本的程式,並用自己的話來『說明』這段程式碼。對我來說,原型的程式碼以及設計上的考量,如同下圖:

標記原本的code 

就算想擷取成介面,也碰到了方法名稱跟參數的type、個數可能不盡相同的困擾。如果介面上開了一堆方法,那就跟原本的Validation沒啥兩樣了。所以,我們要先想個最簡單的介面,也就是這一段一段的if,到底在做什麼?驗證,沒錯,他們都在驗證,只是驗證的東西和邏輯不太一樣。

步驟二:
將驗證的介面定義出來,很簡單,只有一個驗證的方法,回傳bool。而這邊,在驗證後需要知道驗證的錯誤訊息,所以介面就暫訂成下圖:

IValidator

介面很簡單吧,正因為簡單,所以夠抽象,就能廣泛的重複使用且穩定。

步驟三:
介面已經定義出來了,接下來按照原本的程式碼,我們需要驗證的東西有:

  1. Member的Id
  2. Member的Email
  3. Member的Phone
  4. Member的Name


先示範一下MemberIdValidator設計方式:

  1. 定義好一個MemberIdValidator Class
  2. 實作IValidator介面
  3. 將驗證的邏輯寫在Validate的方法中

IdValidator

夠簡單了吧,把驗證錯誤的訊息,記錄在ErrorMessage的屬性,也把驗證是否合法的狀態,記錄在IsValid的屬性中,讓這一個Validator是帶著狀態的。

其他的MemberValidator也都如法炮製,這就是介面的好處,可以讓高階的應用場景抽象地設計高階邏輯,而不用被實作細節侷限住。而實作的細節,又可以達到單一職責原則,內聚力高。在使用上又都透過介面,耦合性低。

步驟四:
重構我們的使用場景,將要驗證Member的各個Validator,加入一個List<IValidator>中,透過foreach,呼叫IValidator的Validate方法,就能將每一種需要驗證的格式都驗證過一遍。

完成版

執行結果:
結果

當未來需要增加新的驗證邏輯,則新增一個新的Validator實作IValidator介面即可。要修改特定的邏輯,也只要修改對應的Validator class內部邏輯。如果在不同的狀況,驗證同一種格式,需要有不同的方式,也只要再透過Strategy Pattern就可以輕鬆的抽換驗證邏輯。

結論

這只是個簡單的示範,重點是未來需求異動的機會可能不低,且期望能讓系統架構更穩定一些。否則,其實寫的Code不會比本來少,但乾淨是一定乾淨的多。

這一篇最後重構完的結果,還有其他重構空間,例如:

  1. List<IValidator>的資料來源,可以透過資料庫或設定檔,再使用Reflection或工廠模式來產生對應的Validator
  2. 驗證規則的部分,可能在不同的情況,也可以透過DB來存取Regular expression的規則。讓未來單純更改Regular Expression的Pattern時,由資料來決定邏輯,而不用修改程式。


老話一句,希望這樣的方式,對大家在整理跟重構系統時,會有所幫助。

Sample Code: LoopAndInterface.zip


[註1]:Program.cs裡面的第15行『var validation = new Validation(); 』是多出來的,重構之後沒有移除,請見諒。(謝謝大Kevin幫忙審校)


點部落-In Joey

↑ Grab this Headline Animator


關連文章

[Design Patterns]使用Interface來實作Template Method Pattern

[Tool]相似度分析- Simian簡介

[測試]自動化測試經驗分享- MS TechDays 2011 BoF內容

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

回應

  • # re: [ASP.NET]重構之路系列v9 –使用介面+迴圈取代不穩定的判斷式 by 小朱

    這是 Command Pattern 的變形吧,只是把 Command.Execute 改成了 Validator.Validate()。

    2011/10/19 上午 12:20 | 回覆

  • # re: [ASP.NET]重構之路系列v9 –使用介面+迴圈取代不穩定的判斷式 by 91

    to 小朱 :

    嗚啊~~被破哏了~不過Command Pattern還沒想到比較有趣的Sample code就是了。
     

    2011/10/19 上午 10:15 | 回覆

  • # re: [ASP.NET]重構之路系列v9 –使用介面+迴圈取代不穩定的判斷式 by hectorlee369

    很棒的一篇文章. 我剛想到的是Spring validator.

    最底層是一些共用的檢核ValidationUtils, 如: EmptyOrSpace, IsNaN ...

    接著是Model的 Validator 如: MemberValidator,

    再配合您範例中的MemberXXValidator.

    並用Inject的方式來組合活用, 未來在維護時可以簡單很多.

     

    2011/10/19 上午 10:47 | 回覆

  • # re: [ASP.NET]重構之路系列v9 –使用介面+迴圈取代不穩定的判斷式 by 91

    to hectorlee369 :

    yup! Spring透過設定檔就可以決定要用哪一些Validator。這就是我提到的還可以重構的空間。

    不過大的framework在推的時候,還是有很多現實考量跟限制啊 (哭哭...)
     

    2011/10/19 下午 12:08 | 回覆

  • # re: [ASP.NET]重構之路系列v9 –使用介面+迴圈取代不穩定的判斷式 by NeiL

    一般驗證不會這麼單純, 會出現驗證相關性, 可以使用 chain of command 或 decorator 來加強結構性.

    2011/10/19 下午 04:34 | 回覆

  • # re: [ASP.NET]重構之路系列v9 –使用介面+迴圈取代不穩定的判斷式 by 91

    to NeiL :

    同意。一步一步來囉。
     

    2011/10/19 下午 04:50 | 回覆

  • # re: [ASP.NET]重構之路系列v9 –使用介面+迴圈取代不穩定的判斷式 by 亞斯狼

    關於這種順序式的驗證資料,其實可以考慮使用Builder樣式;雖然Builder被歸類在建構型的設計樣式,但是把它剖開來看,它在某種狀態下由Builder實做驗證邏輯,由Director決定執行順序與驗證的資料準備,最後再由Director總結驗證結果,這種方式其實也挺適合這個例子的。

    2011/10/19 下午 11:07 | 回覆

  • # re: [ASP.NET]重構之路系列v9 –使用介面+迴圈取代不穩定的判斷式 by 小黑

    很棒的文,期待更多更精闢的文章,
    看你的文章,讓自己開了眼界~

    2011/10/20 上午 09:11 | 回覆

登入後使用進階評論

Please add 4 and 8 and type the answer here: