3

平常我们在工作开发过程中,往往因为工期问题导致整体功能设计考虑的不够周到,导致后期迭代时发现需要原有功能流程基础上追加新功能时,需要耗费更多的成本,无法轻易推翻重构,接着便是将错就错,在if else之下再来一层elseif。所以好代码结构,往往时项目经验积累出来的,会了不代表能,只有实践了、实现了,有实际价值体现了,才能发现学习是一点点积累进步的过程。

而设计模式本身就是通过无数项目积累沉淀而来,适合什么方式什么方案去设计实现功能,往往需要全局的分析,考虑到整体,以及未来的可扩展性等等。

1、场景分析

工作中其实有很多功能是可以使用设计模式的,通过设计模式去优化代码,达到代码的复用性,减少耦合,也就是我们常说的高内聚低耦合。

场景1:商城下单

事情是这样的,小陈接到领导的工作分配,告知要为负责的商城项目实现下单功能,于是小陈是胸有成竹的实现了,由于时间问题,小陈并没有对功能花时间去做过多设计。便有了以下代码:

 @Override
 @Transactional(rollbackFor = ServiceException.class)
 public String createBuyOrders(BuyOrders buyOrders, List<String> couponIds, Long integral, Long integralGym)throws ServiceException{
     productBll.updateSkuStockAndSaleNumber(buyOrders.getBuyOrderProductId(), buyOrders.getBuyOrderSkuId(), buyOrders.getBuyOrderQuantity());
     //订单基础信息
     creatOrderBase(buyOrders);
     //计算订单价格
     normalCalculationOrderPrice(buyOrders);
    //完善地址信息
     setOrderAddress(buyOrders);
     //调用优惠券
     couponBll.useCoupons(buyOrders.getBuyOrderUserId(),buyOrders,couponIds);
     if(!IIntegralInfoBll.buyUseIngegral(buyOrders, integral, integralGym)){
        throw new ServiceException("积分扣除失败,请重新下单");
     }
     if (FavoritesHelper.isNotNull(IBuyOrdersDao.insertBuyOrders(buyOrders))){
         //创建订单关闭订单任务
         if (FavoritesHelper.isNotEmpty(systemVariable)){
            closeTime = systemVariable.getSystemVariableVale();
         }
         quartzService.orderTimeout(buyOrders.getBuyOrderNo(),ISystemVariableBll.ORDER_TIME_OVER);
         return buyOrders.getBuyOrderNo();
     }
     throw new RuntimeExcaption("订单创建失败");
}
过了几周甲方提出了需求,想要实现拼多多那种团购功能,但是因为有优惠了,所以部分优惠券无法使用。小陈觉得没什么压力,于是对新增了一种订单类型:团购订单:

public String createBuyOrders(BuyOrders buyOrders, List<String> couponIds, Long integral, Long integralGym)throws ServiceException{
    if(buyOrders.getType == 1){
        //正常订单;参考上一段代码
    } else if( buyOrders.getType == 2 ){
    //团购订单;具体功能脑部
     //大概实现点:满足设置多少人成为一个团,参加多久人没齐会自动取消,拼团过程中有人退出处理,成功处理,订单生成未支付锁定处理
    } else{
         throw new RuntimeException("错误的订单类型");
    }
}

至此,小陈觉得没啥压力,也就一个方法几百行代码,小问题。

又过了几周,甲方新需求,要有一个秒杀和商品预售功能,小陈顿感压力,还是硬着头皮上了,不就是多几个if else吗,还就没有我小陈ifelseif实现不了的功能了,如果有,就再来一次 else if。

于是乎...:

public String createBuyOrders(BuyOrders buyOrders, List<String> couponIds, Long integral, Long integralGym)throws ServiceException{
     if(buyOrders.getType == 1){
        //正常订单;参考上一段代码
     } else if( buyOrders.getType == 2 ){
        //团购订单;具体功能脑补
        //大概实现点:满足设置多少人成为一个团,参加多久人没齐会自动取消,拼团过程中有人退出处理,成功处理,订单生成未支付锁定处理
     }  else if( buyOrders.getType == 3 ){
        //秒杀订单;具体功能脑补
        //大概实现点:满足条件商品进行秒杀抢购,流量消峰、库存控制、订单生成
     }  else if( buyOrders.getType == 4 ){
        //商品预售订单;具体功能脑补
        //大概实现点:定价膨胀,尾款支付提醒
     } else{
        throw new RuntimeException("错误的订单类型");
     }
}

没等小陈接到甲方的下一个需求,小陈由于可能是个人原因辞职了,于是新来的阿彬接受了小陈的代码,碰巧甲方来了新需求:我想来个周期购,增加我商城的复购率...balabala...

于是阿彬打开先辈的代码一看,好家伙,绝了!这东西我怎么...还没有注释...

2、开始重构

其实向上述的业务情形还有很多,比如与其相对于的商品新增。例如商品填写完基础信息之后需要满足商品的周期购售卖逻辑,需要相对于的商品配置逻辑;相对的团购也是,预售商品更为复杂。我们可以一开始就对这些功能进行合理的规划,然后找出了优化(简化)的点。就算优化不了,也可将整体的代码进行合理的设计,不至于维护起来花费太多的成本。

下单重新实现:

//1首先:定义下单接口
public interface ICreateOrderBll {
​
    void createOrder(BuyOrders orders);
​
}
​
//2其次:实现下单接口
//2.1秒杀
public class KillSaleOrderBllImpl implements ICreateOrderBll {
     @Override
     public void createOrder(BuyOrders orders) {
    ​
     }
}
​
//2.2普通订单
public class NormalOrderBllImpl implements ICreateOrderBll {
     @Override
     public void createOrder(BuyOrders orders) {
    ​
     }
}
​
//3预售订单
public class PreSaleOrderBllImpl implements ICreateOrderBll {
     @Override
     public void createOrder(BuyOrders orders) {
    }
 }
​
//4团购订单
public class GroupSaleOrderBllImpl implements ICreateOrderBll {
     @Override
     public void createOrder(BuyOrders orders) {
    ​
     }
}
​
//5创建工厂方法的工厂
public class BuyOrderFactory {
     
     public ICreateOrderBll getOrderBll(OrderTypeEnum orderType) throws RuntimeException{
         if (ObjectHelper.isEmpty(orderType)) return null;
         else if (orderType.equals(OrderTypeEnum.NORMAL_ORDER)) return new NormalOrderBllImpl();
         else if (orderType.equals(OrderTypeEnum.KILL_SALE)) return new KillSaleOrderBllImpl();
         else if (orderType.equals(OrderTypeEnum.PRE_SALE)) return new PreSaleOrderBllImpl();
         else if (orderType.equals(OrderTypeEnum.GROUP_SALE)) return new GroupSaleOrderBllImpl();
         else throw new RuntimeException("不存在的订单类型");
     }
    }
    //6备注:其中OrderTypeEnum 是定义的订单类型枚举类
 
    ​
    //7调用
    public void createNewOrder(){
         BuyOrders killSaleOrder = new BuyOrders();
        ​
         BuyOrderFactory factory = new BuyOrderFactory();
        ​
         ICreateOrderBll killSaleService = factory.getOrderBll(OrderTypeEnum.KILL_SALE);
         killSaleService.createOrder(killSaleOrder);//秒杀业务
        ​
         BuyOrders preSaleOrder = new BuyOrders();
         ICreateOrderBll preSaleService = factory.getOrderBll(OrderTypeEnum.PRE_SALE);
         preSaleService.createOrder(preSaleOrder);//预售
​
}

自此,下单就重构完成(具体实现根据要求来)。

3、整合SpringBoot

小陈通过学习了解了工厂方法,懂得了其中的些许奥秘,可是奈何项目是SpringBoot,只学了皮毛的小陈照虎画猫,去发现实现的代码,在代码执行到具体的业务实现层却发现空指针,不对啊,不是传进来的对象都有收到啊,怎么没会空指针呢,商品也正常获取啊,怎么回事呢?

通过debug,小陈发现是所写的代码并没有成功注入SpringBoot的IOC中,再调用创建订单时,实现类对象直接报空指针,所以调用方法是抛出了异常。

于是,经过一番努力研读实践,小陈悟了:

//重点圈圈1
@Component
public class BuyOrderFactory {
​
     //重点圈圈2
     @Autowired
     private Map<String,ICreateOrderBll> handlerMap;
    ​
     public ICreateOrderBll getOrderBll(OrderTypeEnum orderType) throws RuntimeException{
        if (ObjectHelper.isEmpty(orderType)) return null;
        else if (orderType.equals(OrderTypeEnum.NORMAL_ORDER)) return handlerMap.get(orderType.toString());
        else if (orderType.equals(OrderTypeEnum.KILL_SALE)) return handlerMap.get(orderType.toString());
        else if (orderType.equals(OrderTypeEnum.PRE_SALE)) return handlerMap.get(orderType.toString());
        else if (orderType.equals(OrderTypeEnum.GROUP_SALE)) return handlerMap.get(orderType.toString());
        else throw new RuntimeException("不存在的订单类型");
     }
​
}
//重点圈圈3 所有实现类也要通过注解让springboot扫描到,此处只举一例,其余写法一样
@Component("GROUP_SALE")
public class GroupSaleOrderBllImpl implements ICreateOrderBll {
    @Override
    public void createOrder(BuyOrders orders) {
    }
 }



​
//调用
 //重点圈圈4
 @Resource
 BuyOrderFactory factory;
 
 public void createNewOrder(){
     BuyOrders killSaleOrder = new BuyOrders();
    ​
     ICreateOrderBll killSaleService = factory.getOrderBll(OrderTypeEnum.KILL_SALE);
     killSaleService.createOrder(killSaleOrder);//秒杀业务
    ​
     BuyOrders preSaleOrder = new BuyOrders();
     ICreateOrderBll preSaleService = factory.getOrderBll(OrderTypeEnum.PRE_SALE);
     preSaleService.createOrder(preSaleOrder);//预售
​
 }

值得一提的是整合springboot时使用的是Map的方式,通过扫描实现类注解所使用的key(上例为@Component("GROUP_SALE"),那么key便是GROUP_SALE),而这个key便是订单类型枚举类的值,如果不使用注解注入,项目会因map无法注入而启动失败。

再后来,小陈由此开始了设计模式学习的不归路。


物干焯的小次郎
9 声望0 粉丝