(8/8)RPC方法的參數,能用枚舉就請考慮枚舉


▄︻┻┳═一Agenda:

▄︻┻┳═一(1/8)[代碼整潔之道]你真的會用枚舉嗎?非也!

▄︻┻┳═一(2/8)枚舉的錯誤用法 之 方法參數

▄︻┻┳═一(3/8)枚舉的錯誤用法 之 方法參數(二)

▄︻┻┳═一(4/8)枚舉的錯誤用法 之 方法返回值

▄︻┻┳═一(5/8)枚舉的錯誤用法 之 方法體內部

▄︻┻┳═一(6/8)枚舉的錯誤用法 之 分支判斷

▄︻┻┳═一(7/8)借助枚舉說一下數據類型定義規范

▄︻┻┳═一(8/8)RPC方法的參數,能用枚舉就請考慮枚舉


 

我們都知道,在做后台管理系統開發時,很多功能的管理頁都有一堆查詢條件,用來篩選業務數據記錄。其中,最常見的一個,當屬業務數據的狀態了。比如,用戶狀態、企業狀態、任務狀態,每人都能從自己的系統里找出一大堆。

先問大家一個問題:對於一些列表頁,查詢全部狀態的數據,你給服務端接口傳的狀態值是什么?

不用說,大家一般會傳:"0" 或者 "ALL"  或者 空串。

 

【言歸正傳】

我們是ToB的系統。

 

 

 如上圖,在企業管理頁,有一個篩選條件是product,意為企業所開通的業務產品,比如機票、酒店、保險。程序common包里定義了ProductEnum。

public enum ProductEnum {
    AirTicket("AIRTICKET","機票"),
    Hotel("HOTEL","酒店"),
    Insurance("INSURANCE","保險"),
    ;
    
    ...
}

 

網站采用的是前后端分離,后端通過rpc調用dubbo接口。任務分工方面,其中一同學寫dubbo服務;另一同學寫頁面前后端,調用dubbo服務獲取數據。

我在codereview時,發現獲取企業數據的dubbo服務接口EnterpriseService是如下定義的:

public interface EnterpriseService {

    /**
     * 獲取企業分頁數據
     * @param pageNo
     * @param pageSize
     * @param vo
     * @param product 業務線
     * @return
     */
    Result getPage(int pageNo, int pageSize, EnterpriseVO vo,String product);
}

其中,product參數是String。

web端呢,直接獲取頁面vo,然后調用這個dubbo接口

    @GetMapping(value = "/list")
    public Result<?> queryPageList(EnterpriseVO enterprise,
                                   @RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
                                   @RequestParam(name="pageSize", defaultValue="10") Integer pageSize,
                                   HttpServletRequest req) {
        long start = System.currentTimeMillis();
        try {
            log.info("企業管理查詢開始>>>>>>>>");
            String product;
            if(StringUtils.isEmpty(enterprise.getProduct())){
                product = "";
            }else{
                product = enterprise.getProduct();
            }
            return enterpriseService.getPage(pageNo, pageSize ,enterprise,product);
        } finally {
            log.info("查詢企業列表,耗時={}",System.currentTimeMillis()-start);
        }
    }
View Code

 

我的直覺是,product參數為什么不用ProductEnum來限定呢。然后,再看接口實現類,發現這個getPage方法里直接將product參數賦值給了pojo。然后就調用o/rm框架的query方法了。

 

當前端頁面查詢所有product的企業時,頁面傳的值,不管是"0" 或者 "ALL"  或者 空串,都會致使查的數據有誤呀。

 

終於,墨菲定律又奏效了!在QA測試的時候,bug暴露出來了。

 

【重構后的代碼】

dubbo接口——EnterpriseService.getPage

Result getPage(int pageNo, int pageSize, EnterpriseVO vo,@Nullable ProductEnum product);

 

web服務端——EnterpriseController.queryPageList

    @GetMapping(value = "/list")
    public Result<?> queryPageList(EnterpriseVO enterprise,
                                   @RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
                                   @RequestParam(name="pageSize", defaultValue="10") Integer pageSize,
                                   HttpServletRequest req) {
        long start = System.currentTimeMillis();
        try {
            log.info("企業管理查詢開始>>>>>>>>");
            if (StringUtils.isEmpty(enterprise.getProduct())) {
                return enterpriseService.getPage(pageNo, pageSize, enterprise, null);
            } else {
                ProductEnum productEnum = ProductEnum.getBean(enterprise.getProduct());
                return enterpriseService.getPage(pageNo, pageSize, enterprise, productEnum);
            }
        } finally {
            log.info("查詢企業列表,分頁列表查詢,查詢企業數據,耗時={}",System.currentTimeMillis()-start);
        }
    }
View Code

 

【總結】

我們可以看到,當product改成枚舉類型ProductEnum之后,就限定了調用方只能傳特定的枚舉值。不像String那樣開放,就像我前文闡述的,靈活自由往往會帶來更多隱患。

再者,你定義了String,調用方不清楚當全部的時候傳什么,可能就按自己固有的方式來傳值了。在復雜的查詢邏輯里,這樣的bug並不是一測就能測出來的。QA把bug指出來時,就老實修復吧,別說調用你接口的同學的傳值不對了。所以,給大家一個忠告,寫代碼能不隨意就別隨意。大家好才是真的好。

 

【題外話】

曾經在做支付系統的出款時,因為我們起初缺乏安全意識,整個出款方法有做異常捕獲,當捕獲到異常后,即會把出款單的付款狀態改為失敗。無獨有偶,碰巧,某個上游支付渠道http接口響應超級慢,頻繁導致socket響應超時。socket超時,並不代表服務方沒有處理完成。所以,可怕的事情出現了,有一批付款單,渠道方是付款成功,我方呢,因為http超時而置為了付款失敗。然后,商戶得知是付款失敗后,重新發起了出款。。。一晚上兩個小時的時間,重復出款金額達到8萬!

以上這種情況,沒有經歷過的人,也許體會不到我們當時的五味陳雜。當然,這不是重點,我要說明的是,這種損失,我們責怪人家接口,是不起任何作用的,反而讓人聽着有推卸之嫌。打鐵還需自身硬,綉花要得手綿巧,練好基本功很重要!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM