zoukankan      html  css  js  c++  java
  • 【一起学设计模式】命令模式+模板方法+工厂方法实战: 如何优雅的更新商品库存...

    前言

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

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

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

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

    业务背景

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

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

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

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

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

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

    类图

    7BE68636-56B3-412C-8367-7F5C9B3E1161.png

    代码实现

    • 商品库存更新命令接口
      这里采用的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 和公众号:壹枝花算不算浪漫,如若转载请标明来源!

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

    22.jpg

  • 相关阅读:
    Java高并发8-计算机内存模式以及volatile,sychronized工作原理
    Java高并发7-inheritableThreadLocal实现父子线程变量同步原理
    Java高并发6-ThreadLocal内部各种方法实现原理
    Java高并发5-守护线程、ThreadLocal和死锁四个必要条件
    Java高并发4-解析volatile关键字
    AQS详解,并发编程的半壁江山
    Java 调用File的delete方法删除文件返回false
    Java 将文件夹打成压缩包 zip
    前端 文件夹上传 解决方案
    ORA-00904: "FILED_TYPE": 标识符无效
  • 原文地址:https://www.cnblogs.com/wang-meng/p/11996439.html
Copyright © 2011-2022 走看看