Spring是一個底層framework,幫我們處理 IoC 的事情,為支撐這個framework,裡面也開發了很多非常好用的 Utils,研究他的source過程中,無意中發現有些好用的Components,再對照自己先前寫好的一堆 Utils 中,發現原來自己遇到的問題,其實人家早就遇過,而且也早已開發了更多、更豐富、功能更強大的元件,這些元件很多都已切開並可獨立引用的,於是我就挑了其中一個 Assert 小元件來幫忙處理資料驗證。不過有很多還是中冠特有的需求,所以我繼寫了支class繼承它,再加入符合中冠需求之功能,完成了這支 com.icsc.dpms.de.dejcAssert資料驗證公用元件。
介紹這個元件前,可先看看iThome上的這篇文章-打造穩固程式碼,先從思路方向著手,文中提出了Assertion 的觀念,我用比較白話一點的比喻來說明好了,自從911事件之後,航空安檢就變得很嚴格,他們必須保證上飛機的客人都是「安全的客人」,所以他們都會針對上飛機的客人做足各式各樣的安全測試-有沒有帶小刀、液體…等,通過才能上飛機。而我們的程式就像一台飛機,送進來的參數就是乘客,要確保你的程式正常運行,必須對進來的參數作嚴格檢查。在「軟體預先架構之美學」一書中所提到「別讓冷空氣進來」,就是類似這種「保護自己」的觀念。
理論大家都知道,但實作上有時就顯得很累贅,以下面程式為例,為確保qty 數量必須要大於0,所以寫了藍色字體檢查 :
|
/**
* 庫存總金額處理邏輯
* @param inventoryType 品別,qty 數量
*/
public boolean
countAmt(dsjccom dsCom, String inventoryType, BigDecimal qty)
{
if (
qty.compareTo( new BigDecimal("0"))==0 ) {
throw new
Exception(“使用平均金額交易,交易數量不可能為
}
}
|
寫起來很囉嗦吧,若想讓你的程式更穩固,還要再檢查另兩個參數,看他們是否恐佈份子。
|
public boolean
countAmt(dsjccom dsCom, String inventoryType, BigDecimal qty)
{
if ( dsCom==null
) {
throw new
Exception(“dsCom 不可為 null”) ;
}
if ( inventoryType==null
) {
throw new Exception(“庫別不可為null”) ;
}
if (
qty.compareTo( new BigDecimal("0"))==0 ) {
throw new
Exception(“使用平均金額交易,交易數量不可能為
}
}
|
噢! 寫一堆,還沒真正進入核心邏輯,寫得這麼煩,真得很難提高大家檢查參數的意願。
有鑑於此,這個dejcAssert元件的任務,就是讓大家在做上述檢查邏輯時變得輕鬆易用又簡單,我們看看怎麼用,上面的需求,其實3行就搞定 !
|
public boolean
countAmt(dsjccom dsCom, String inventoryType, BigDecimal qty)
{
dejcAssert.notNull( dsCom, “dsCom 不可為 null”);
dejcAssert.notNull( inventoryType, “庫別不可為 null”);
dejcAssert.notZero( qty, “使用平均金額交易,交易數量不可能為
}
|
為何變這麼簡單? 因為程式將異常部份用 Exception 拋出去了,所以壓根兒不需用 if 一個一個檢查,尤其在跟帳務、庫存有關的系統中,為確保帳務沒問題,通常會做很多的數字檢查,諸如大於0、小於0、不等於0,或A數量必須大於B數量之類的比較,其中尤其大家都用BigDecimal來處理數量,而BigDecimal要比大小又不像一般數字比較式如: a>b 這麼簡單,
例如我要檢查 if ( qty <
0 ),就要寫成 if (
qty.compareTo(new BigDecimal(“0” ))==
-1 ) ,這種寫法超級不直覺。而很不幸,這類跟數字有關的判斷充斥在ERP的每個角落,為讓程式碼更好讀,更容易寫,dejcAssert
專責提供一些常用的判斷,寫來更直覺,更easy !
範例 1 : 不可等於0
|
使用前
|
if (
qty.compareTo( new BigDecimal(“
throw new Exception(“庫存量不得等於
}
|
|
使用後
|
dejcAssert.notZero(
qty , “庫存量不得等於
|
|
說明
|
若發生錯誤會拋出 IllegalArgumentException,此 exception 是 runtime exception,不會強制 catch 的,用的人不會有 catch
exception 壓力及負擔
|
範例 2 : 數字A必須大於數字B
|
使用前
|
if (
numA.compareTo( numB ) !=1 ) {
throw new Exception(“A庫存量[“+numA+”]不得少於B庫存量[“+numB+”]”) ;
}
|
|
使用後
|
dejcAssert.greater(
numA, numB, “A庫存量{0}不得少於B庫存量{1}”) ;
|
|
說明
|
方法的取名都是第1個參數相對第2個參數。
其中錯誤訊息都應該顯示出實際錯誤數字,以利用戶判斷錯誤,但要組合這種訊息,寫起來很麻煩,要把變數加來加去,雙引號括來括去,不好寫也不好讀,所以這元件會順便提供將送入之參數自動放到{n}之中,
其中 n=0..參數數量-1 |
下面是目前有提供的各類方法
|
afterOrEquals(java.lang.String date1,
java.lang.String date2, java.lang.String msg)
date1 必須在 date2 之後,但2個日期可相同 |
|
beforeOrEquals(java.lang.String date1,
java.lang.String date2, java.lang.String msg)
date1 必須在 date2 之前,但2個日期可相同 |
|
betweenPeriod(java.lang.String fromDate, java.lang.String fromTime,
java.lang.String toDate, java.lang.String toTime,
java.lang.String date, java.lang.String time,
java.lang.String msg)
驗證 date,time 必須界於 fromDate,fromTime 及 toDate,toTime 之間 (含邊界) |
|
equals(java.math.BigDecimal num1, java.math.BigDecimal num2,
java.lang.String msg)
驗證 num1 必須等於 num2 |
|
greater(java.math.BigDecimal num1,
java.math.BigDecimal num2, java.lang.String msg)
驗證 num1 必須大於 num2 |
同時因為本 class 是繼承 Spring 的 org.springframework.util.Assert
,所以 dejcAssert 也會提供下列 Spring Assert 本來就有的功能:
|
doesNotContain(String textToSearch, String substring)
Assert that the given text does not contain the given substring. |
|
doesNotContain(String textToSearch, String substring, String message)
Assert that the given text does not contain the given substring. |
|
isAssignable(Class superType, Class subType)
Assert that superType.isAssignableFrom(subType) is true. |
|
isAssignable(Class superType, Class subType, String message)
Assert that superType.isAssignableFrom(subType) is true. |
|
isInstanceOf(Class clazz, Object obj)
Assert that the provided object is an instance of the provided class. |
|
isInstanceOf(Class type, Object obj, String message)
Assert that the provided object is an instance of the provided class. |
|
isTrue(boolean expression)
Assert a boolean expression, throwing IllegalArgumentException if the test result is false. |
|
notEmpty(Collection collection)
Assert that a collection has elements; that is, it must not be null and must have at least one element. |
|
notEmpty(Collection collection, String message)
Assert that a collection has elements; that is, it must not be null and must have at least one element. |
|
state(boolean expression)
Assert a boolean expression, throwing IllegalStateException if the test result is false. |
有了這麼輕便的驗證資料方法,希望大家以後都要有驗證參數的習慣,擴大來應用的話,不僅要在執行程式邏輯之前要驗證,執行之後也最好再驗證一次。例如庫存一般在領料後會再做一次檢查,以確保結餘的庫存量是安全的,只要每人都養成重要邏輯的「前」與「後」驗證資料的習慣,「中冠品質、堅若盤石」的境界,相信指日可待。
附註:
資料必須驗證之場合
一、由外來系統傳入之變數 (如財會收集各AP拋入之帳務資料…)
二、由通訊傳入之資料 (如 DI …)
三、舉凡開放給別人呼叫的API而傳入之參數
四、設計成獨立 Component 所傳入之參數,雖然有時是被自己呼叫,但因設計時已將之視為獨立運作之 Component,所以不管是自己或其它系統使用它,對Component而言,傳入之參數都是「陌生人」,都需被檢查。
五、分析時已找出有明確之「後置條件」者,就需針對「後置條件」作檢驗,例如【領料】的後置條件為「庫存量必須大於0」,因此在【領料】後就需檢查庫存量。
不必驗證之場合:
一、很清楚所傳入之參數來源,例如private方法之參數,顯然只有自己才能使用,而參數也是自己製造並傳入的,此時就沒必要又做一堆檢查了,畢竟檢查,還是多少會花點系統資源的。
沒有留言:
張貼留言