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

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

前言
好久沒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幫忙審校)


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