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

前言

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

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

之前为了学习设计模式,看过网上很多相关博客讲解,大都是画下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

posted @ 2019-12-06 17:09  一枝花算不算浪漫  阅读(1111)  评论(4编辑  收藏  举报