[ASP.NET]重構之路系列v9 –使用介面+迴圈取代不穩定的判斷式
前言
好久沒PO文了,深潛了一段時間,努力的吸取大型系統經驗跟學習更底子的知識,雖然累但也是感覺收穫滿滿。最近報名了iT邦幫忙的第四屆鐵人鍛鍊賽,打算把之前一些跟系統品質、重構、測試等相關文章整理一系列,希望屆時也可以比較有條理的整理在wiki上。
回到主題,今天要分享的部分,是把一陀很類似卻又攤在程式碼中的判斷式,想辦法抽象化。例子會有點像[ASP.NET]重構之路系列v7 –簡化判斷式這一篇,但相信這次舉的例子會更貼近大家在系統上碰到的需求。
需求說明
- 有多筆會員資料要經過多種格式的驗證
- 最後需得到驗證的結果,以及驗證錯誤的訊息
直覺設計出來的原型程式就會長的像下面一樣:
只是將原本的Validation,簡單的做個殼而已。
這邊的程式,至少已經將每個驗證的邏輯抽到了Validation的Class中,將可能變化的驗證邏輯封裝起來。我得強調一下,這樣的程式並沒有『錯』,但這邊提到的是已知未來一定會增加新的驗證邏輯,如何設計面對這樣的需求,不會動到主程式商業邏輯,就是這篇的目的。
重構步驟
步驟一:
首先把我們的眼睛瞇起來一點,(或是你要打開萬花筒寫輪眼、白眼、輪迴眼、血紅眼都可以),用抽象化的方式來看原本的程式,並用自己的話來『說明』這段程式碼。對我來說,原型的程式碼以及設計上的考量,如同下圖:
就算想擷取成介面,也碰到了方法名稱跟參數的type、個數可能不盡相同的困擾。如果介面上開了一堆方法,那就跟原本的Validation沒啥兩樣了。所以,我們要先想個最簡單的介面,也就是這一段一段的if,到底在做什麼?驗證,沒錯,他們都在驗證,只是驗證的東西和邏輯不太一樣。
步驟二:
將驗證的介面定義出來,很簡單,只有一個驗證的方法,回傳bool。而這邊,在驗證後需要知道驗證的錯誤訊息,所以介面就暫訂成下圖:
介面很簡單吧,正因為簡單,所以夠抽象,就能廣泛的重複使用且穩定。
步驟三:
介面已經定義出來了,接下來按照原本的程式碼,我們需要驗證的東西有:
- Member的Id
- Member的Email
- Member的Phone
- Member的Name
先示範一下MemberIdValidator設計方式:
- 定義好一個MemberIdValidator Class
- 實作IValidator介面
- 將驗證的邏輯寫在Validate的方法中
夠簡單了吧,把驗證錯誤的訊息,記錄在ErrorMessage的屬性,也把驗證是否合法的狀態,記錄在IsValid的屬性中,讓這一個Validator是帶著狀態的。
其他的MemberValidator也都如法炮製,這就是介面的好處,可以讓高階的應用場景抽象地設計高階邏輯,而不用被實作細節侷限住。而實作的細節,又可以達到單一職責原則,內聚力高。在使用上又都透過介面,耦合性低。
步驟四:
重構我們的使用場景,將要驗證Member的各個Validator,加入一個List<IValidator>中,透過foreach,呼叫IValidator的Validate方法,就能將每一種需要驗證的格式都驗證過一遍。
執行結果:
當未來需要增加新的驗證邏輯,則新增一個新的Validator實作IValidator介面即可。要修改特定的邏輯,也只要修改對應的Validator class內部邏輯。如果在不同的狀況,驗證同一種格式,需要有不同的方式,也只要再透過Strategy Pattern就可以輕鬆的抽換驗證邏輯。
結論
這只是個簡單的示範,重點是未來需求異動的機會可能不低,且期望能讓系統架構更穩定一些。否則,其實寫的Code不會比本來少,但乾淨是一定乾淨的多。
這一篇最後重構完的結果,還有其他重構空間,例如:
- List<IValidator>的資料來源,可以透過資料庫或設定檔,再使用Reflection或工廠模式來產生對應的Validator
- 驗證規則的部分,可能在不同的情況,也可以透過DB來存取Regular expression的規則。讓未來單純更改Regular Expression的Pattern時,由資料來決定邏輯,而不用修改程式。
老話一句,希望這樣的方式,對大家在整理跟重構系統時,會有所幫助。
Sample Code: LoopAndInterface.zip
[註1]:Program.cs裡面的第15行『var validation = new Validation(); 』是多出來的,重構之後沒有移除,請見諒。(謝謝大Kevin幫忙審校)
blog 與課程更新內容,請前往新站位置:http://tdd.best/