一枝花算不算浪漫

一枝花算不算浪漫

前言

之前在我的博客(一枝花算不算浪漫)中已经更新过两篇设计模式相关的内容

上面内容都是基于真实业务场景精简后的设计(工作中真实场景使用到的)。

之前为了学习设计模式,看过网上很多相关博客讲解,大都是画下UML类图,举例几个毫不相干的demo,看了几遍仍然是云里雾里。

学习设计模式只有在真正的业务场景去使用才会更好的理解其精髓。这里举例自己工作中电商的业务场景,然后配合一些业务功能的实现,来学会设计模式,使自己的代码更优雅。

业务背景

在一个电商或者进销存业务中,我们都有库存的概念。
更新库存又分为很多种场景:购买、退货、下单、取消订单、加购物车等等

当然,我们之前也见过策略模式,这种业务可以抽象为每个策略去做。但是这里我们使用新的设计模式来尝试完成它。

  1. 命令模式command
    设置一系列的command命令,我们将不同类型的库存更新逻辑,封装了不同的库存更新命令。

    命令模式还有一个很经典的场景,就是做这个命令撤销。如果我们在执行这个命令的过程中,发现命令中的某个步骤失败了,我们可以在command里面实现一套cancel的逻辑,撤销这个命令之前做的所有操作,对已经完成的好做执行反步骤。

  2. 模板方法模式
    将一些通用的步骤抽取到抽象基类,另外一个基于模板的模式限定了每个库存更新的过程都是一样的,按照一样的步骤和顺序走,很清晰。后面如果要修改更新库存的逻辑,或者hi新增一种库存更新的逻辑,都是按照一样的步骤和顺序去走。

  3. 工厂方法模式
    工厂方法模式,就是将工厂模式和模板方法模式,结合起来。
    就是说,可能我们需要的不是一个工厂,不同的工厂创建不同的产品,但是这些工厂之间有一些通用的逻辑,可以抽取到父工厂里面去,子工厂就专注于自己的事情就可以了。

类图

【一起学设计模式】命令模式+模板方法+工厂方法实战: 如何优雅的更新商品库存...-LMLPHP

代码实现

  • 商品库存更新命令接口
    这里采用的command命令模式
/**
 * 商品库存更新命令的接口
 *
 * @author wangmeng
 * @blog https://www.cnblogs.com/wang-meng/
 * @create 2019-12-05 06:42
 **/
public interface StockUpdater {

    /**
     * 更新商品库存
     * @return 处理结果
     */
    Boolean updateGoodsStock();
}
  • 创建更新命令command的工厂接口
    这里采用的是工厂方法模式
/**
 * 库存更新命令工厂接口
 *
 * @author wangmeng
 * @blog https://www.cnblogs.com/wang-meng/
 * @create 2019-12-05 06:42
 **/
public interface StockUpdaterFactory<T> {

    /**
     * 创建一个库存更新命令
     *
     * @param parameter 参数对象
     * @return 库存更新命令
     */
    StockUpdater create(T parameter);
}
  • 商品库存更新命令的抽象基类
/**
 * 商品库存更新命令的抽象基类
 *
 * @author wangmeng
 * @blog https://www.cnblogs.com/wang-meng/
 * @create 2019-12-05 06:44
 **/
@Slf4j
public abstract class AbstractStockUpdater implements StockUpdater{

    /**
     * 商品库存对象集合
     */
    protected List<InventoryGoodsStock> goodsStockList;

    /**
     * 商品库存管理模块service
     */
    protected InventoryGoodsStockService goodsStockService;

    public AbstractStockUpdater(List<InventoryGoodsStock> goodsStockList, InventoryGoodsStockService goodsStockService) {
        this.goodsStockList = goodsStockList;
        this.goodsStockService = goodsStockService;
    }

    /**
     * 更新商品库存
     * @return
     */
    @Override
    public Boolean updateGoodsStock() {
        try {
            updateSaleStockQuantity();
            updateLockedStockQuantity();
            updateSaledStockQuantity();
            updateStockStatus();
            updateGmtModified();
            executeUpdateGoodsStock();
        } catch (Exception e) {
            log.error("error", e);
        }
        return true;
    }

    /**
     * 更新商品的销售库存
     * @throws Exception
     */
    protected abstract void updateSaleStockQuantity() throws Exception;

    /**
     * 更新商品的锁定库存
     * @throws Exception
     */
    protected abstract void updateLockedStockQuantity() throws Exception;

    /**
     * 更新商品的已销售库存
     * @throws Exception
     */
    protected abstract void updateSaledStockQuantity() throws Exception;

    /**
     * 更新商品的库存状态
     */
    private void updateStockStatus() throws Exception {
        for(InventoryGoodsStock goodsStockDO : goodsStockList) {
            if(goodsStockDO.getSaleStockQuantity() > 0L) {
                goodsStockDO.setStockStatus(StockStatus.IN_STOCK);
            } else {
                goodsStockDO.setStockStatus(StockStatus.NOT_IN_STOCK);
            }
        }
    }

    /**
     * 更新商品库存的修改时间
     */
    private void updateGmtModified() throws Exception {
        Date current = new Date();
        for(InventoryGoodsStock goodsStockDO : goodsStockList) {
            goodsStockDO.setGmtModified(current);
        }
    }

    /**
     * 实际执行更新商品库存的操作
     * @throws Exception
     */
    private void executeUpdateGoodsStock() throws Exception {
        for(InventoryGoodsStock goodsStockDO : goodsStockList) {
            goodsStockService.updateById(goodsStockDO);
        }
    }
}
  • 抽象创建command的工厂类
/**
 * @author wangmeng
 * @blog https://www.cnblogs.com/wang-meng/
 * @create 2019-12-05 06:56
 **/
@Slf4j
public abstract class AbstractStockUpdaterFactory<T> implements StockUpdaterFactory<T> {

    protected InventoryGoodsStockService stockService;

    public AbstractStockUpdaterFactory(InventoryGoodsStockService stockService) {
        this.stockService = stockService;
    }

    @Override
    public StockUpdater create(T parameter) {
        try {
            List<Long> goodsSkuIds = getGoodsSkuIds(parameter);
            List<InventoryGoodsStock> goodsStockDOs = createGoodsStockList(goodsSkuIds);
            return create(goodsStockDOs, parameter);
        } catch (Exception e) {
            log.error("error", e);
        }
        return null;
    }

    /**
     * 获取商品sku id集合
     * @param parameter 参数
     * @return 商品sku id集合
     * @throws Exception
     */
    protected abstract List<Long> getGoodsSkuIds(T parameter) throws Exception;

    /**
     * 创建库存更新命令
     * @param parameter 参数
     * @param goodsStockDOs 商品库存DO对象集合
     * @return 库存更新命令
     * @throws Exception
     */
    protected abstract StockUpdater create(
            List<InventoryGoodsStock> goodsStockDOs, T parameter) throws Exception;

    /**
     * 创建商品库存DO对象集合
     *
     * @param goodsSkuIds 商品sku id集合
     * @return 商品库存对象集合
     */
    private List<InventoryGoodsStock> createGoodsStockList(List<Long> goodsSkuIds) throws Exception {
        List<InventoryGoodsStock> goodsStocks = new ArrayList<>(goodsSkuIds.size());
        EntityWrapper<InventoryGoodsStock> wrapper = new EntityWrapper<>();
        wrapper.in("goods_sku_id", goodsSkuIds);
        List<InventoryGoodsStock> goodsStockList = stockService.selectList(wrapper);
        Map<Long, InventoryGoodsStock> stockMap = new HashMap<>();
        if (!CollectionUtils.isEmpty(goodsStockList)) {
            stockMap = goodsStockList.stream().collect(Collectors.toMap(InventoryGoodsStock::getGoodsSkuId, Function.identity()));
        }
        for (Long goodsSkuId : goodsSkuIds) {
            InventoryGoodsStock inventoryGoodsStock = stockMap.get(goodsSkuId);
            if (inventoryGoodsStock == null) {
                inventoryGoodsStock = createGoodsStock(goodsSkuId);
                // 不建议循环中操作sql,这里只做演示作用,实际可以批量操作sql
                stockService.insert(inventoryGoodsStock);
            }

            goodsStocks.add(inventoryGoodsStock);
        }

        return goodsStocks;
    }

    /**
     * 创建商品库存DO对象
     * @param goodsSkuId 商品sku id
     * @return 商品库存DO对象
     */
    private InventoryGoodsStock createGoodsStock(Long goodsSkuId) {
        InventoryGoodsStock goodsStockDO = new InventoryGoodsStock();
        goodsStockDO.setGoodsSkuId(goodsSkuId);
        goodsStockDO.setSaleStockQuantity(0L);
        goodsStockDO.setLockedStockQuantity(0L);
        goodsStockDO.setSaledStockQuantity(0L);
        goodsStockDO.setStockStatus(StockStatus.NOT_IN_STOCK);
        goodsStockDO.setGmtCreate(new Date());
        goodsStockDO.setGmtModified(new Date());
        return goodsStockDO;
    }
}
  • 采购入库库存更新命令的工厂
/**
 * @author wangmeng
 * @blog https://www.cnblogs.com/wang-meng/
 * @create 2019-12-05 06:56
 **/
@Slf4j
public abstract class AbstractStockUpdaterFactory<T> implements StockUpdaterFactory<T> {

    protected InventoryGoodsStockService stockService;

    public AbstractStockUpdaterFactory(InventoryGoodsStockService stockService) {
        this.stockService = stockService;
    }

    @Override
    public StockUpdater create(T parameter) {
        try {
            List<Long> goodsSkuIds = getGoodsSkuIds(parameter);
            List<InventoryGoodsStock> goodsStockDOs = createGoodsStockList(goodsSkuIds);
            return create(goodsStockDOs, parameter);
        } catch (Exception e) {
            log.error("error", e);
        }
        return null;
    }

    /**
     * 获取商品sku id集合
     * @param parameter 参数
     * @return 商品sku id集合
     * @throws Exception
     */
    protected abstract List<Long> getGoodsSkuIds(T parameter) throws Exception;

    /**
     * 创建库存更新命令
     * @param parameter 参数
     * @param goodsStockDOs 商品库存DO对象集合
     * @return 库存更新命令
     * @throws Exception
     */
    protected abstract StockUpdater create(
            List<InventoryGoodsStock> goodsStockDOs, T parameter) throws Exception;

    /**
     * 创建商品库存DO对象集合
     *
     * @param goodsSkuIds 商品sku id集合
     * @return 商品库存对象集合
     */
    private List<InventoryGoodsStock> createGoodsStockList(List<Long> goodsSkuIds) throws Exception {
        List<InventoryGoodsStock> goodsStocks = new ArrayList<>(goodsSkuIds.size());
        EntityWrapper<InventoryGoodsStock> wrapper = new EntityWrapper<>();
        wrapper.in("goods_sku_id", goodsSkuIds);
        List<InventoryGoodsStock> goodsStockList = stockService.selectList(wrapper);
        Map<Long, InventoryGoodsStock> stockMap = new HashMap<>();
        if (!CollectionUtils.isEmpty(goodsStockList)) {
            stockMap = goodsStockList.stream().collect(Collectors.toMap(InventoryGoodsStock::getGoodsSkuId, Function.identity()));
        }
        for (Long goodsSkuId : goodsSkuIds) {
            InventoryGoodsStock inventoryGoodsStock = stockMap.get(goodsSkuId);
            if (inventoryGoodsStock == null) {
                inventoryGoodsStock = createGoodsStock(goodsSkuId);/**
 * 采购入库库存更新命令的工厂
 * @author wangmeng
 *
 */
@Component
public class PurchaseInputStockUpdaterFactory<T>
        extends AbstractStockUpdaterFactory<T> {

    /**
     * 构造函数
     * @param stockService 商品库存管理模块的service组件
     */
    @Autowired
    public PurchaseInputStockUpdaterFactory(InventoryGoodsStockService stockService) {
        super(stockService);
    }

    /**
     * 获取商品sku id集合
     * @return 商品sku id集合
     * @throws Exception
     */
    @Override
    protected List<Long> getGoodsSkuIds(T parameter) throws Exception {
        PurchaseInputOrderDTO purchaseInputOrderDTO = (PurchaseInputOrderDTO) parameter;
        List<PurchaseInputOrderItemDTO> purchaseInputOrderItemDTOs =
                purchaseInputOrderDTO.getItems();

        if(purchaseInputOrderItemDTOs == null || purchaseInputOrderItemDTOs.size() == 0) {
            return new ArrayList<>();
        }

        List<Long> goodsSkuIds = new ArrayList<Long>(purchaseInputOrderItemDTOs.size());
        for(PurchaseInputOrderItemDTO purchaseInputOrderItemDTO : purchaseInputOrderItemDTOs) {
            goodsSkuIds.add(purchaseInputOrderItemDTO.getGoodsSkuId());
        }

        return goodsSkuIds;
    }

    /**
     * 创建库存更新命令
     * @param goodsStockDOs 商品库存DO对象集合
     * @return 库存更新命令
     * @throws Exception
     */
    @Override
    protected StockUpdater create(List<InventoryGoodsStock> goodsStockDOs, T parameter) throws Exception {
        PurchaseInputOrderDTO purchaseInputOrderDTO = (PurchaseInputOrderDTO) parameter;
        List<PurchaseInputOrderItemDTO> purchaseInputOrderItemDTOs = purchaseInputOrderDTO.getItems();
        Map<Long, PurchaseInputOrderItemDTO> purchaseInputOrderItemDTOMap = new HashMap<>();
        if(purchaseInputOrderItemDTOs != null && purchaseInputOrderItemDTOs.size() > 0) {
            for(PurchaseInputOrderItemDTO purchaseInputOrderItemDTO : purchaseInputOrderItemDTOs) {
                purchaseInputOrderItemDTOMap.put(purchaseInputOrderItemDTO.getGoodsSkuId(),
                        purchaseInputOrderItemDTO);
            }
        }

        return new PurchaseInputStockUpdater(goodsStockDOs, stockService, purchaseInputOrderItemDTOMap);
    }

}
                // 不建议循环中操作sql,这里只做演示作用,实际可以批量操作sql
                stockService.insert(inventoryGoodsStock);
            }

            goodsStocks.add(inventoryGoodsStock);
        }

        return goodsStocks;
    }

    /**
     * 创建商品库存DO对象
     * @param goodsSkuId 商品sku id
     * @return 商品库存DO对象
     */
    private InventoryGoodsStock createGoodsStock(Long goodsSkuId) {
        InventoryGoodsStock goodsStockDO = new InventoryGoodsStock();
        goodsStockDO.setGoodsSkuId(goodsSkuId);
        goodsStockDO.setSaleStockQuantity(0L);
        goodsStockDO.setLockedStockQuantity(0L);
        goodsStockDO.setSaledStockQuantity(0L);
        goodsStockDO.setStockStatus(StockStatus.NOT_IN_STOCK);
        goodsStockDO.setGmtCreate(new Date());
        goodsStockDO.setGmtModified(new Date());
        return goodsStockDO;
    }
}
  • 采购入库库存更新命令
/**
 * 采购入库库存更新命令
 * @author zhonghuashishan
 *
 */
public class PurchaseInputStockUpdater extends AbstractStockUpdater {

    /**
     * 采购入库单条目DTO集合
     */
    private Map<Long, PurchaseInputOrderItemDTO> purchaseInputOrderItemDTOMap;

    /**
     * 构造函数
     * @param goodsStockDOs 商品库存DO对象
     * @param stockService 商品库存管理模块的service组件
     */
    public PurchaseInputStockUpdater(
            List<InventoryGoodsStock> goodsStockDOs,
            InventoryGoodsStockService stockService,
            Map<Long, PurchaseInputOrderItemDTO> purchaseInputOrderItemDTOMap) {
        super(goodsStockDOs, stockService);
        this.purchaseInputOrderItemDTOMap = purchaseInputOrderItemDTOMap;
    }

    /**
     * 更新销售库存
     */
    @Override
    protected void updateSaleStockQuantity() throws Exception {
        for(InventoryGoodsStock goodsStockDO : goodsStockList) {
            PurchaseInputOrderItemDTO purchaseInputOrderItemDTO =
                    purchaseInputOrderItemDTOMap.get(goodsStockDO.getGoodsSkuId());
            goodsStockDO.setSaleStockQuantity(goodsStockDO.getSaleStockQuantity()
                    + purchaseInputOrderItemDTO.getArrivalCount());
        }
    }

    /**
     * 更新锁定库存
     */
    @Override
    protected void updateLockedStockQuantity() throws Exception {

    }

    /**
     * 更新已销售库存
     */
    @Override
    protected void updateSaledStockQuantity() throws Exception {

    }

}
  • 实际流转调用代码
/**
 * <p>
 * 库存中心的商品库存表 服务实现类
 * </p>
 *
 * @author wangmeng
 * @since 2019-12-03
 */
@Service
@Slf4j
public class InventoryGoodsStockServiceImpl extends ServiceImpl<InventoryGoodsStockMapper, InventoryGoodsStock> implements InventoryGoodsStockService {

    /**
     * 采购入库库存更新命令工厂
     */
    @Autowired
    private PurchaseInputStockUpdaterFactory<PurchaseInputOrderDTO> purchaseInputStockUpdateCommandFactory;

    /**
     * 通知库存中心,“采购入库完成”事件发生了
     * @param purchaseInputOrderDTO 采购入库单DTO
     * @return 处理结果
     */
    @Override
    public Boolean informPurchaseInputFinished(
            PurchaseInputOrderDTO purchaseInputOrderDTO) {
        try {
            StockUpdater goodsStockUpdateCommand = purchaseInputStockUpdateCommandFactory.create(purchaseInputOrderDTO);
            goodsStockUpdateCommand.updateGoodsStock();
        } catch (Exception e) {
            log.error("error", e);
            return false;
        }
        return true;
    }
}

申明

本文章首发自本人博客:https://www.cnblogs.com/wang-meng 和公众号:壹枝花算不算浪漫,如若转载请标明来源!

感兴趣的小伙伴可关注个人公众号:壹枝花算不算浪漫

【一起学设计模式】命令模式+模板方法+工厂方法实战: 如何优雅的更新商品库存...-LMLPHP

12-07 10:03