本周API開發進行到了資料檢核的部分,隨著商品的投保規則越多,相對程式中判斷的邏輯就變得很多,以下是需求單位所需要的投保規則
1. 員工投保後其眷屬才能加保
2. 投保年齡限制在15歲~75歲
3. 眷屬投保保額不得超過員工本人
4. 投保職業類別限制第1~4類
5. 新投保之被保險人需填寫健康告知書
6. 本人投保保額若有提高需填寫健康告知書
所以當程式設計師看到以上規則時,心中所想的程式碼架構大致如下(方法一),其實就是把上述的中文規則直接翻譯成程式碼
public String checkRule(String 員工ID, String 投保年齡, String 投保保額, String 投保職業類別){// 若員工投保後其眷屬才能加保
if(員工已加保){
// 投保年齡15~75歲才能加保
if(15<= 年齡 && 年齡<=75){
// 眷屬投保保額未超過員工本人
if(眷屬保額 <= 員工保額){
// 投保職業類別在1~4類間
if(1 <= 職業類別 && 職業類別 <=4 ){ // 新投保之被保險人需填寫健康告知書
if(新投保){
// 需填寫健康告知書
}
else{
// 不需填寫健康告知書
} // 本人投保保額若有提高需填寫健康告知書
if(本人投保保額提高){
// 需填寫健康告知書
}
else{
// 不需填寫健康告知書
}
return "可以投保"; }
// 投保職業類別非1~4類間
else{
return "不能投保";
} }
// 眷屬投保保額超過員工本人
else{
return "不能投保";
}
}
// 投保年齡不在15~75歲區間
else{
return "不能投保";
}
}
// 員工未加保
else{
return "不能投保";
}
}
其中會發現需要很多的IF-ELSE 的條件判斷式才能完成是否可以投保的判斷,這會造成以下的問題:
程式碼不易閱讀,導致日後人工維護時很困難
因此可以思考做如下的邏輯轉換(方法二): 採用排除法
public String checkRule(String 員工ID, String 投保年齡, String 投保保額, String 投保職業類別){ // 先假設所有的被保險人都可以加保
String result = "可加保";
// 假設所有的被保險人都不用填寫健康告知書
String result = "不須填寫";
// 員工未投保
if(員工未投保){
return "不可加保";
}
// 投保年齡不在 15歲 ~ 75歲 間
if(年齡<15 || 年齡>75){
return "不可加保";
}
// 眷屬投保保額超過員工本人
if(眷屬保額 > 員工保額){
return "不可加保";
}
// 投保職業類別非1~4類
if(職業類別<1 || 職業類別 >4){
return "不可加保";
}
// 新投保之被保險人 或 本人投保保額有提高
if(新投保之被保險人 || 保額提高){
// 需填寫健康告知書
}
return "可加保";
}
經過簡化後會發現程式的行數(篇幅)減少了許多,且更容易閱讀和維護,仔細一看不只是只有篇幅變少,其實思維方式也做了改變,和方法一相比,這邊採用的是排除法,先假設所有的被保險人都可以加保,然後利用排除的方式把不能加保的情況做排除,只要有符合不能加保的任一條件,就直接做回傳(return)
這邊就是使用到上面連結文章中,所提到的負向表列方式來撰寫程式
另外在方法二的程式寫法,只有出現IF而沒有ELSE語句,其實這樣的寫法有相關的文獻可以參考。原來這樣的寫法有特別的名詞叫做 guard clause,用英文單字來看也挺有意義,就像是你家樓下有一個警衛,負責將非法人士擋在門外,確保住戶安全,對照程式碼來看,IF就是層層的警衛,而RETURN 就是把人擋在外面不放行。
平常寫程式都不太會注意到這塊,總以為只要可以跑就好,以上兩個範例雖然執行結果相同,但閱讀起來就有很大的差異,因此寫程式也必須考慮到日後的可維護性,當IF-ELSE 出現的次數過多時,就可以思考是不是可以做邏輯上的收斂,正向判斷如果過多,可以思考用反向的方式來做。