­

程式碼優化實戰,3行程式碼解決了一百個if else!

事情是這樣的,前段時間做程式碼review的時候,發現項目中有一個方法程式碼量超雞兒多,而且大部分都是寫的參數校驗的程式碼,得,我們先抓著縷一縷需求先。

產品需求

找到產品要到了需求文檔,需求是這樣得:

  • excel數據模板下載
  • excel數據導入
  • 導入得時候根據模板得校驗規則來進行篩選,導入成功得返回成功列表,數據有問題得返回失敗列表,失敗列表支援數據編輯修正

好吧。看到需求第一眼可能就是第三列有點難度,我們知道,傳統得數據校驗是在DTO上面加註解

如下:

//第一種
public Result test(@RequestBody @Validated TestDTO dto) {...}
//第二種
public  Result  test(@RequestBody @Valid TestDTO dto{...}
//第三種
public Result  test(@RequestBody @Validated(value = {SaveGroup.class}) TestDTO dto) {...}

TestDTO裡面呢會有一些類似 @NotNull@NotBlank@Size等校驗註解,這裡就不列了。

然後再在全局異常攔截那裡進行統一封裝,使其放回得數據結構盡量保持統一,所以一般還得有一個GlobalExceptionHandle

image-20200809162534227

image-20200809162624232

講到常見得數據校驗,那麼我們畫風一轉,再回來看需求,可見以上需求是不滿足得,首先,我們入參是一個文件,也就是用戶傳得那個excel,我們得先解析文件再進行數據判斷,合法得放一個集合,不合法得放一個集合,再者,即使入參是一個數組,這種校驗一旦不滿足立馬進異常處理了,無法返回給前端正確得數據結構,所以引入了我們今天解決這類需求得解決方案。

重構開始-開篇

我們以之前寫文章裡面得一個項目easyexcel-demo為模板進行程式碼得改造和編寫

程式碼地址://github.com/pengziliu/GitHub-code-practice

image-20200809163432162

下載之前做的小demo,運行起來,創建一個工作簿導入數據

創建一份Excel數據

image-20200809173342671

PostMan模擬調用數據解析

image-20200809173308231

項目程式碼和控制台輸出

image-20200809173613381

重構開始-實戰

好吧,上面介紹了一下之前項目得基本讀取excel功能,我們就基於以上功能來實現我們開篇所說得需求。

我們對手機號和姓名自定義一下規則:

  • 手機號滿足基本手機號規則
  • 姓名非空且不能超過四個字元

返回成功失敗兩個集合,全部滿足得返回到成功,只要有一條不滿足得丟入失敗列表。

定義返回得數據結構

新建返回對象UserExcelVO.java

image-20200809174433991

好了,兄弟們,這裡我要上同事寫的偽程式碼了。坐好扶穩了!!!

@PostMapping("/importExcel")
    public UserExcelVO importExcel(@RequestParam("file") MultipartFile file){
        List<UserExcelModel> list = null;
        List<UserExcelModel> fail = new ArrayList<>();
        UserExcelVO userExcelVO = new UserExcelVO();
        String mobieReg = "^[1][3,4,5,7,8][0-9]{9}$$";
        try {
            list = EasyExcel.read(file.getInputStream(),UserExcelModel.class,new ModelExcelListener()).sheet().doReadSync();

            list.forEach(data->{
                //處理姓名的校驗
                if(StringUtils.isEmpty(data.getName())||data.getName().length()> 4 ){
                    fail.add(data);
                    return;
                }
                //處理手機號的校驗
                if (StringUtils.isEmpty(data.getMobile())|| !data.getMobile().matches(mobieReg)) {
                    fail.add(data);
                    return;
                }
                //以下根據欄位多少可能有n個if
            });
            userExcelVO.setFail(fail);
            list.removeAll(fail);
            userExcelVO.setSuccess(list);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return userExcelVO;
    }

測試數據:

用戶名	年齡	手機號	性別
寶典哥1	11	23847235	男
寶典哥2	12	15813847236	男
寶典哥3	13	15813847237	男
寶典哥4	14	15813847238	男
寶典哥5	15	15813847239	男
寶典哥6	16	15813847240	男
寶典哥7	17	152247241	男
寶典哥8	18	15813847242	男
寶典哥9	19	15813847243	男
寶典哥10	20	15813847244	男
寶典哥11	21	15813847245	男
寶典哥12	22	15813847246	男
寶典哥13	23	15813847247	男
寶典哥14	24	15813847248	男
寶典哥15	25	15813847249	男

測試結果:

{
    "success": [
        {
            "cellStyleMap": {},
            "name": "寶典哥2",
            "age": 12,
            "mobile": "15813847236",
            "sex": "男"
        },
        {
            "cellStyleMap": {},
            "name": "寶典哥3",
            "age": 13,
            "mobile": "15813847237",
            "sex": "男"
        },
        {
            "cellStyleMap": {},
            "name": "寶典哥4",
            "age": 14,
            "mobile": "15813847238",
            "sex": "男"
        },
        {
            "cellStyleMap": {},
            "name": "寶典哥5",
            "age": 15,
            "mobile": "15813847239",
            "sex": "男"
        },
        {
            "cellStyleMap": {},
            "name": "寶典哥6",
            "age": 16,
            "mobile": "15813847240",
            "sex": "男"
        },
        {
            "cellStyleMap": {},
            "name": "寶典哥8",
            "age": 18,
            "mobile": "15813847242",
            "sex": "男"
        },
        {
            "cellStyleMap": {},
            "name": "寶典哥9",
            "age": 19,
            "mobile": "15813847243",
            "sex": "男"
        }
    ],
    "fail": [
        {
            "cellStyleMap": {},
            "name": "寶典哥1",
            "age": 11,
            "mobile": "23847235",
            "sex": "男"
        },
        {
            "cellStyleMap": {},
            "name": "寶典哥7",
            "age": 17,
            "mobile": "152247241",
            "sex": "男"
        },
        {
            "cellStyleMap": {},
            "name": "寶典哥10",
            "age": 20,
            "mobile": "15813847244",
            "sex": "男"
        },
        {
            "cellStyleMap": {},
            "name": "寶典哥11",
            "age": 21,
            "mobile": "15813847245",
            "sex": "男"
        },
        {
            "cellStyleMap": {},
            "name": "寶典哥12",
            "age": 22,
            "mobile": "15813847246",
            "sex": "男"
        },
        {
            "cellStyleMap": {},
            "name": "寶典哥13",
            "age": 23,
            "mobile": "15813847247",
            "sex": "男"
        },
        {
            "cellStyleMap": {},
            "name": "寶典哥14",
            "age": 24,
            "mobile": "15813847248",
            "sex": "男"
        },
        {
            "cellStyleMap": {},
            "name": "寶典哥15",
            "age": 25,
            "mobile": "15813847249",
            "sex": "男"
        }
    ]
}

根據測試結果應該是問題不大的,我這裡也是模擬一下,但是實際的業務場景,一個excel裡面假如是訂單數據,最少是幾十個欄位起步的,難道要寫幾十個if else ,明顯是不合理的,那我們能不能使用註解的方式幫我們解決問題呢,如果使用註解的話應該如何使用呢?

開造!

創建ValidationUtils.java

public class ValidationUtils {

    public static Validator getValidator(){
        return validator;
    }

    static Validator validator;
    static{
        ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
        validator=validatorFactory.getValidator();
    }
}

對Model加註解

image-20200809181934329

對Controller進行改寫

 @PostMapping("/v2/importExcel")
    public UserExcelVO importExcelV2(@RequestParam("file") MultipartFile file){
        List<UserExcelModel> list = null;
        List<UserExcelModel> fail = new ArrayList<>();
        UserExcelVO userExcelVO = new UserExcelVO();
        try {
            list = EasyExcel.read(file.getInputStream(),UserExcelModel.class,new ModelExcelListener()).sheet().doReadSync();
            list.forEach(data->{
            	//此處3行程式碼解決了一百個if else
                Set<ConstraintViolation<UserExcelModel>> violations  =  ValidationUtils.getValidator().validate(data);
                if(violations.size()>0){
                    fail.add(data);
                }
            });
            userExcelVO.setFail(fail);
            list.removeAll(fail);
            userExcelVO.setSuccess(list);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return userExcelVO;
    }

測試

對同一組數據進行測試

image-20200809182924261

測試結果如下,可以發現,兩種實現數據輸出結果一致

{
    "success": [
        {
            "cellStyleMap": {},
            "name": "寶典哥2",
            "age": 12,
            "mobile": "15813847236",
            "sex": "男"
        },
        {
            "cellStyleMap": {},
            "name": "寶典哥3",
            "age": 13,
            "mobile": "15813847237",
            "sex": "男"
        },
        {
            "cellStyleMap": {},
            "name": "寶典哥4",
            "age": 14,
            "mobile": "15813847238",
            "sex": "男"
        },
        {
            "cellStyleMap": {},
            "name": "寶典哥5",
            "age": 15,
            "mobile": "15813847239",
            "sex": "男"
        },
        {
            "cellStyleMap": {},
            "name": "寶典哥6",
            "age": 16,
            "mobile": "15813847240",
            "sex": "男"
        },
        {
            "cellStyleMap": {},
            "name": "寶典哥8",
            "age": 18,
            "mobile": "15813847242",
            "sex": "男"
        },
        {
            "cellStyleMap": {},
            "name": "寶典哥9",
            "age": 19,
            "mobile": "15813847243",
            "sex": "男"
        }
    ],
    "fail": [
        {
            "cellStyleMap": {},
            "name": "寶典哥1",
            "age": 11,
            "mobile": "23847235",
            "sex": "男"
        },
        {
            "cellStyleMap": {},
            "name": "寶典哥7",
            "age": 17,
            "mobile": "152247241",
            "sex": "男"
        },
        {
            "cellStyleMap": {},
            "name": "寶典哥10",
            "age": 20,
            "mobile": "15813847244",
            "sex": "男"
        },
        {
            "cellStyleMap": {},
            "name": "寶典哥11",
            "age": 21,
            "mobile": "15813847245",
            "sex": "男"
        },
        {
            "cellStyleMap": {},
            "name": "寶典哥12",
            "age": 22,
            "mobile": "15813847246",
            "sex": "男"
        },
        {
            "cellStyleMap": {},
            "name": "寶典哥13",
            "age": 23,
            "mobile": "15813847247",
            "sex": "男"
        },
        {
            "cellStyleMap": {},
            "name": "寶典哥14",
            "age": 24,
            "mobile": "15813847248",
            "sex": "男"
        },
        {
            "cellStyleMap": {},
            "name": "寶典哥15",
            "age": 25,
            "mobile": "15813847249",
            "sex": "男"
        }
    ]
}

程式碼倉庫

//github.com/pengziliu/GitHub-code-practice

最新程式碼已提交,歡迎star,裡面包含很多的項目教程和實例

總結

寫程式碼的時候,除了做功能,應該要考慮程式碼的擴展性,不然產品說加個功能,我們又得吭哧吭哧寫程式碼,那這樣也台悲催了。