最近有一个需求、简要概述就是,我负责提供配件服务的一个公共的库存接口,给配件这个服务多个关于要操作到库存的接口都会调用我的这个接口去操作库存。
对于库存这块大家应该也不熟悉,其实,刚开始接到组长分给我的这个任务的时候我还是蛮忐忑的,因为配件这块服务,库存是一个大头,为了确保到库存的正常出入库,这涉及到很多东西,用到了java的并发编程进行异步化处理,数据库的乐观锁机制处理多线程并发更新数据。其实,这里我还引入了最近比较火的一个分布式锁实现方案redission,因为自己引入了这个,我还跟项目经理吵了一架,但我还是跟他讲明了其中的利弊,对于分布式锁以及并发编程这块的内容后面我会单独写一个博客来总结一下我使用这个redission遇到的坑,以及并发去执行出入库的弊端以及注意事项。
首先,从最简单的开始,因为我这个是一个接口对应多种业务,一开始大家肯定就是这种想法,无非就是调用方传入一个操作类型进行,然后我对操作类型写多个ifelse嘛,这个简单,就像下面这个样子。
public Map<String,Map<Object,Object>> partCommonStorageIn(List<Map> partList, String storageCode, String dealerCode, int operateType,String sheetNo)
throws ServiceBizException, ExecutionException, InterruptedException {
// 基本参数检验
checkParam(partList,storageCode,dealerCode,operateType);
Map<String,Map<Object,Object>> result = null;
//采购入库
if (operateType == PartDictCodeConstants.PART_BUY_IN){
result = purchaseStock(partList,storageCode,dealerCode,operateType,sheetNo);
}else if(operateType == PartDictCodeConstants.PART_BORROW_IN){
//配件借进入库
result = borrowStock(partList,storageCode,dealerCode,operateType,sheetNo);
}else if(operateType == PartDictCodeConstants.PART_RETURN_IN){
//配件借出归还入库
result = lendReturnStock(partList,storageCode,dealerCode,operateType,sheetNo);
}else if(operateType == PartDictCodeConstants.DICT_IN_OUT_TYPE_TRANSFER){
//配件移库入库
result = transferStock(partList,storageCode,dealerCode,operateType,sheetNo);
}else if(operateType == PartDictCodeConstants.PART_PROFIT){
//配件报溢入库
result = partReportOverflow(partList,storageCode,dealerCode,operateType,sheetNo);
}
return result;
}
看起来,思路清晰,if,else分支也很清楚,但不觉得代码很臃肿,维护起来麻烦吗,尤其是其他人来接锅的时候,连看下去的欲望都没有了。这时候你需要用策略模式消除其中的if else,进行一下简单的重构!
策略模式
1、首先抽象业务处理器
public abstract class InspectionSolver {
public abstract void solve(Long orderId, Long userId);
public abstract String[] supports();
}
2、将业务处理器和其支持处理的类型放到一个容器中,java里Map就是最常用的容器之一
@Component
public class InspectionSolverChooser implements ApplicationContextAware{
private Map<String, InspectionSolver> chooseMap = new HashMap<>();
public InspectionSolver choose(String type) {
return chooseMap.get(type);
}
@PostConstruct
public void register() {
Map<String, InspectionSolver> solverMap = context.getBeansOfType(InspectionSolver.class);
for (InspectionSolver solver : solverMap.values()) {
for (String support : solver.supports()) {
chooseMap.put(support,solver);
}
}
}
private ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.context=applicationContext;
}
}
这里是在应用启动的时候,加载spring容器中所有InspectionSolver类型的处理器,放到InspectionSolverChooser的map容器中。注意是InspectionSolver类型,所以定义的处理器都得继承InspectionSolver,其次是spring容器中的才能加载,所以定义的处理器都得放到spring容器中(@Component注解不能少),其实这里的思路也很简单,通过实现ApplicationContextAware接口拿到上下文对象,然后通过这个上下文对象拿到在容器中对象不同的库存业务实现类,然后在这个类型统一管理。
3、定义不同的处理器
@Component
public class ChangeWarehouseSolver extends InspectionSolver {
@Override
public void solve(Long orderId, Long userId) {
System.out.println("订单"+orderId+"开始进行采购入库加库存了。。");
}
@Override
public String[] supports() {
return new String[] {InspectionConstant.INSPECTION_TASK_TYPE_BATCH_CHANGE_WAREHOUSE};
}
}
@Component
public class ChangeShippingSolver extends InspectionSolver{
@Override
public void solve(Long orderId, Long userId) {
System.out.println("订单"+orderId+"开始进行扣减库存了。。");
}
@Override
public String[] supports() {
return new String[] {InspectionConstant.INSPECTION_TASK_TYPE_BATCH_CHANGE_SHIPPING};
}
}
@Component
public class ReplaceOrderGoodsSolver extends InspectionSolver{
@Override
public void solve(Long orderId, Long userId) {
System.out.println("订单"+orderId+"开始进行替换商品了");
}
@Override
public String[] supports() {
return new String[]{InspectionConstant.INSPECTION_TASK_TYPE_BATCH_REPLACE_ORDER_GOODS};
}
}
4、测试类
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes=Application.class)// 指定spring-boot的启动类
public class InspectionTest {
@Autowired
private InspectionSolverChooser chooser;
@Test
public void test() throws Exception{
//准备数据
String taskType = InspectionConstant.INSPECTION_TASK_TYPE_BATCH_CHANGE_WAREHOUSE;
Long orderId = 12345L;
Long userId = 123L;
//获取任务类型对应的solver
InspectionSolver solver = chooser.choose(taskType);
if (solver == null) {
throw new RuntimeException("任务类型暂时无法处理!");
}
//调用不同solver的方法进行处理
solver.solve(orderId,userId);
}
}
测试类中我消除了可能一长段的if else,从选择器InspectionSolverChooser中根据type的不同取出不同的任务处理器InspectionSolver,然后调用其solve()方法进行任务处理,不同处理器调用的当然就是不同的solve()方法了,目的达到。
其实,一开始我组长也是让我就按照ifelse这样子写,先别考虑那么多,但是对于一个有技术追求的我,肯定不会专业那样子的啦,其实我也是今天才开始打算专门写一栏博客来记录自己的成长记录,加油!!!