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

前言

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

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

以前爲了學習設計模式,看過網上不少相關博客講解,大都是畫下UML類圖,舉例幾個絕不相干的demo,看了幾遍仍然是雲裏霧裏。sql

學習設計模式只有在真正的業務場景去使用纔會更好的理解其精髓。這裏舉例本身工做中電商的業務場景,而後配合一些業務功能的實現,來學會設計模式,使本身的代碼更優雅。設計模式

業務背景

在一個電商或者進銷存業務中,咱們都有庫存的概念。
更新庫存又分爲不少種場景:購買、退貨、下單、取消訂單、加購物車等等app

固然,咱們以前也見過策略模式,這種業務能夠抽象爲每一個策略去作。可是這裏咱們使用新的設計模式來嘗試完成它。ide

  1. 命令模式command
    設置一系列的command命令,咱們將不一樣類型的庫存更新邏輯,封裝了不一樣的庫存更新命令。函數

    命令模式還有一個很經典的場景,就是作這個命令撤銷。若是咱們在執行這個命令的過程當中,發現命令中的某個步驟失敗了,咱們能夠在command裏面實現一套cancel的邏輯,撤銷這個命令以前作的全部操做,對已經完成的好作執行反步驟。學習

  2. 模板方法模式
    將一些通用的步驟抽取到抽象基類,另一個基於模板的模式限定了每一個庫存更新的過程都是同樣的,按照同樣的步驟和順序走,很清晰。後面若是要修改更新庫存的邏輯,或者hi新增一種庫存更新的邏輯,都是按照同樣的步驟和順序去走。this

  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

相關文章
相關標籤/搜索