【一起学设计模式】命令模式+模板方法+工厂方法实战: 如何优雅的更新商品库存...
前言
之前在我的博客(一枝花算不算浪漫)中已经更新过两篇设计模式相关的内容
- 【一起学设计模式】策略模式实战一:基于消息发送的策略模式实战
- 【一起学习设计模式】策略模式实战二:配合注解 干掉业务代码中冗余的if else...
- 【一起学设计模式】访问者模式实战:权限管理树删除节点操作
上面内容都是基于真实业务场景精简后的设计(工作中真实场景使用到的)。
之前为了学习设计模式,看过网上很多相关博客讲解,大都是画下UML类图,举例几个毫不相干的demo,看了几遍仍然是云里雾里。
学习设计模式只有在真正的业务场景去使用才会更好的理解其精髓。这里举例自己工作中电商的业务场景,然后配合一些业务功能的实现,来学会设计模式,使自己的代码更优雅。
业务背景
在一个电商或者进销存业务中,我们都有库存的概念。
更新库存又分为很多种场景:购买、退货、下单、取消订单、加购物车等等
当然,我们之前也见过策略模式,这种业务可以抽象为每个策略去做。但是这里我们使用新的设计模式来尝试完成它。
-
命令模式command
设置一系列的command命令,我们将不同类型的库存更新逻辑,封装了不同的库存更新命令。命令模式还有一个很经典的场景,就是做这个命令撤销。如果我们在执行这个命令的过程中,发现命令中的某个步骤失败了,我们可以在command里面实现一套cancel的逻辑,撤销这个命令之前做的所有操作,对已经完成的好做执行反步骤。
-
模板方法模式
将一些通用的步骤抽取到抽象基类,另外一个基于模板的模式限定了每个库存更新的过程都是一样的,按照一样的步骤和顺序走,很清晰。后面如果要修改更新库存的逻辑,或者hi新增一种库存更新的逻辑,都是按照一样的步骤和顺序去走。 -
工厂方法模式
工厂方法模式,就是将工厂模式和模板方法模式,结合起来。
就是说,可能我们需要的不是一个工厂,不同的工厂创建不同的产品,但是这些工厂之间有一些通用的逻辑,可以抽取到父工厂里面去,子工厂就专注于自己的事情就可以了。
类图
代码实现
- 商品库存更新命令接口
这里采用的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 和公众号:壹枝花算不算浪漫,如若转载请标明来源!
感兴趣的小伙伴可关注个人公众号:壹枝花算不算浪漫