为什么对研发情有独钟

   相信很多猿仔都和我一样,学到知识总想着在实际工作中快速用到,这样就算暴露处理错误,也方便我们加深对知识的理解。     不才,年初想着使用策略+模板方法的设计模式去重构我们项目中臃肿的代码,上去就是一通撸。经过自测之后,满意地露出了淫荡的笑容...

    然而,之后的工作时间中,总会有一些匪夷所思的数据出现,比如,短信接收者吐槽不是他的数据,为何他收到短信;再者,数据不一致等现象。

    仔细检查代码,也没发现我这惊为天人的代码逻辑有何不妥之处,而且自己反复测试还是没有问题复现,故想着莫非用户他们自己误操作后,通过业务删除了数据?又或是运营同事误操作导致数据丢失?一阵阵臆想后,有些心烦意乱。可总不能把这些没凭证的想法作为问题的解决方案啊,所以我开始冷静下来,仔细查看历史日志的输出情况,对比寻找规律。

    先谈谈我重构的业务,订单状态变更接口。

    要知道,订单状态是有很多的,而且每一个状态都代表的一种业务逻辑,所以,如果我们把订单状态变更的接口写在一个方法中,这个方法长的有些不忍直视。有猿仔会想到每一个订单状态下的逻辑封装成一个方法,然后这样看上去就简洁很多。

    这确实是种书写风格,也就是门面/外观的设计模式呗。但是要知道,订单的状态是很多的,对比不同状态做不同处理的话,使用if分支结构去做判断,还是会使代码不雅观。作为高逼格的程序猿,不会允许这种事情出现。

    所以我想着使用策略解决多重if判断,使用模板方法抽取订单状态变更中重复的代码。这样组合,可以很好地满足开闭原则(对扩展开发,对修改关闭)。因为就算以后改代码,起码业务层代码不用动,只需要改对应的订单状态策略实现类即可。

    思路是对的,实际却干了一件蠢事。我把所有订单策略实现类注入到IOC中,通过枚举列举订单状态对应的实现类,这样当我们请求携带订单状态访问接口时,策略上下文对象就会根据枚举找到对应的实现类的beanId,进而从IOC中拿到这个策略实现类,然后通过模版方法的方式执行业务。

@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void statusChange(List<OrdStatusVo> ordStatusVos) throws Exception {
    if (CollectionUtils.isEmpty(ordStatusVos)) {
        return;
    }
    // 循环执行订单变更的业务
    for (OrdStatusVo ordStatusVo : ordStatusVos) {
        ordChangeStatusApiContext.doService(ordStatusVo);
    }
}



/**
 * 按照状态映射对应的实现,执行其业务逻辑
 *
 * @param ordStatusVo
 * @throws Exception
 */
public void doService(OrdStatusVo ordStatusVo) throws Exception {
    String beanId = OrdStatusBeanIdMappingApiEnum.getBeanIdByStatus(ordStatusVo.getStatus());
    if (beanId == null) {
        throw new Exception(“订单状态异常”);
    }

// 获取订单状态变更策略实现类
    OrdChangeStatusApiEvent event = springUtils.getBean(beanId, OrdChangeStatusApiEvent.class);
    if(event == null){
        throw new Exception(“订单状态异常”);

}

// 执行业务方法
    event.doService(ordStatusVo);
}

此处,代码还算严谨,而且问题的根源也不在此。那么,我们来看看我的模板类是什么样的吧。

public abstract class OrdChangeStatusApiEvent {
    /**

     * 订单ID

     */

    protected Long ordId;

    /**

     * 接口数据

     */

    protected OrdStatusVo ordStatusVo;

    public void doService(OrdStatusVo ordStatusVo) throws Exception {

        // 初始化公共参数
        initParams(ordStatusVo);

        // 修改订单状态
        changeStatus();
    }


    protected void changeStatus() throws Exception {

       // 修改订单状态

    }

    protected void initParams(OrdStatusVo ordStatusVo) throws Exception {
        // 初始化公共参数
        this.ordStatusVo = ordStatusVo;
        this.ordId = ordStatusVo.getOrderId();
    }
}

     也就是说,我在模版类中定义了一个属性订单ID,然后在策略实现类调用业务方法时,在模版中对其初始化。很多小伙伴可能认为这么做其实没什么问题,但我犯的错误在于,向IOC注入策略对象时,没有做特殊处理。

     要知道,Spring默认帮我们创建的对象都是单例的,也就是说,每次从IOC获取的对象都是同一个。那么,我把策略实现类的属性订单ID赋值后,后面的请求过来会做覆盖。这就是为什么我自己测试没有问题的原因。我测试的数据都是单条的,也就是属于前一个请求处理完,后一个请求才会过来把之前的属性订单ID做修改。当有并发时,就会出现前一个请求尚未处理完,其属性订单ID已经发生变化,拿着这个已经变动的数据做处理,就会造成一系列的BUG。

     分析至此,冷汗直流。赶快告知老大问题所在,然后在每个实现类上采用原型(scope=prototype)去解决这一问题。虽是一次事故,但也让我很好地理解了这三种设计模式以及SpringIOC在使用过程中需要注意的问题。最大的成就感,在于冷静分析之后的成果。

研发就是不断埋雷扫雷的过程。生活不会总是那么平淡,遇到问题,只要冷静,我们都是侦探!

 

欢迎大家和帝都的雁积极互动,头脑交流会比个人埋头苦学更有效!共勉!

公众号:帝都的雁

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值