zoukankan      html  css  js  c++  java
  • (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万!

    以上这种情况,没有经历过的人,也许体会不到我们当时的五味陈杂。当然,这不是重点,我要说明的是,这种损失,我们责怪人家接口,是不起任何作用的,反而让人听着有推卸之嫌。打铁还需自身硬,绣花要得手绵巧,练好基本功很重要!

  • 相关阅读:
    ETL开发
    ETL 运行维护
    软件开发的核心
    索引的本质
    也谈谈学习
    oracle内部结构
    [kuangbin带你飞]专题十六 KMP & 扩展KMP & Manacher H
    [kuangbin带你飞]专题十六 KMP & 扩展KMP & Manacher :G
    [kuangbin带你飞]专题十六 KMP & 扩展KMP & Manacher F
    [kuangbin带你飞]专题十六 KMP & 扩展KMP & Manacher E
  • 原文地址:https://www.cnblogs.com/buguge/p/12810301.html
Copyright © 2011-2022 走看看