【一起學設計模式】命令模式+模板方法+工廠方法實戰: 如何優雅的更新商品庫存......

前言

之前在我的博客(一枝花算不算浪漫)中已經更新過兩篇設計模式相關的內容

上面內容都是基於真實業務場景精簡後的設計(工作中真實場景使用到的)。

之前爲了學習設計模式,看過網上很多相關博客講解,大都是畫下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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章