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

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

前言

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

  • 【一起學設計模式】策略模式實戰一:基於訊息傳送的策略模式實戰
  • 【一起學習設計模式】策略模式實戰二:配合註解 幹掉業務程式碼中冗餘的if else...
  • 【一起學設計模式】訪問者模式實戰:許可權管理樹刪除節點操作

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

之前為了學習設計模式,看過網上很多相關部落格講解,大都是畫下UML類圖,舉例幾個毫不相干的demo,看了幾遍仍然是雲裡霧裡。

學習設計模式只有在真正的業務場景去使用才會更好的理解其精髓。這裡舉例自己工作中電商的業務場景,然後配合一些業務功能的實現,來學會設計模式,使自己的程式碼更優雅。

業務背景

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

當然,我們之前也見過策略模式,這種業務可以抽象為每個策略去做。但是這裡我們使用新的設計模式來嘗試完成它。

  1. 命令模式command
    設定一系列的command命令,我們將不同型別的庫存更新邏輯,封裝了不同的庫存更新命令。

    命令模式還有一個很經典的場景,就是做這個命令撤銷。如果我們在執行這個命令的過程中,發現命令中的某個步驟失敗了,我們可以在command裡面實現一套cancel的邏輯,撤銷這個命令之前做的所有操作,對已經完成的好做執行反步驟。

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

  3. 工廠方法模式
    工廠方法模式,就是將工廠模式和模板方法模式,結合起來。
    就是說,可能我們需要的不是一個工廠,不同的工廠建立不同的產品,但是這些工廠之間有一些通用的邏輯,可以抽取到父工廠裡面去,子工廠就專注於自己的事情就可以了。

類圖

程式碼實現

  • 商品庫存更新命令介面
    這裡採用的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 和公眾號:壹枝花算不算浪漫,如若轉載請標明來源!

感興趣的小夥伴可關注個人公眾號:壹枝花算不算浪漫