运用战略方式处理多条件问题

问题重现

这是公司代码里边的一个接口,我需求依据type的不同,去决议要不要存储里边的方针。

ini拷贝代码 @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean saveDimensionsByQuestionBankId(List<MbDimensionsDto> dimensions, Long questionBankId,Integer type) {
        //假设是无维度,说明不用保存
        if(type == QuestionBankSettingTypeEnum.无维度.getValue()){
            return true;
        }
        //假设不是,就先查验
        checkIsDimensionsDtoLegal(dimensions);
        if(type.equals(QuestionBankSettingTypeEnum.一极维度.getValue())){
            dimensions.forEach(mbDimensionDto -> {
                MbDimension mbDimension = new MbDimension();
                BeanUtils.copyProperties(mbDimensionDto, mbDimension);
                mbDimension.setQuestionBankId(questionBankId);
                this.save(mbDimension);
            });
            return true;
        }
        if(type.equals(QuestionBankSettingTypeEnum.二级维度.getValue())){
            //新增树状dimensions
            dimensions.forEach(mbDimensionDto -> {
                MbDimension mbDimension = new MbDimension();
                BeanUtils.copyProperties(mbDimensionDto, mbDimension);
                mbDimension.setQuestionBankId(questionBankId);
                this.save(mbDimension);
                Long fatherId = mbDimension.getId();
                List<MbDimensionsDto> children = mbDimensionDto.getChildren();
                if(children == null){
                    return;
                }
                children.forEach(son -> {
                    MbDimension mbDimensionSon = new MbDimension();
                    BeanUtils.copyProperties(son, mbDimensionSon);
                    mbDimensionSon.setQuestionBankId(questionBankId);
                    //新增后的dimension的id,即为chidren dimension的父亲id.
                    mbDimensionSon.setParentId(fatherId);
                    this.save(mbDimensionSon);
                });
            });
            return true;
        }
        return false;
    }

不得不说,恰当的丑陋!!

有一个学长的话在我脑子中不断回响:“写代码就得高雅!!”

所以我重拾起他说了很多次的战略方式。

实战

界说战略接口和完结类

关于保存维度这个方法,我们无妨给他设置一个接口。

php拷贝代码/**
 * @author ht
 */
public interface SaveDimensionStrategy {
    /**
     * 其时的维度方式,是无维度仍是一级维度,仍是二级维度
     * @return
     */
    Integer getType();
    /**
     * 依据其时维度,进行处理
     * @param dimensions
     * @param questionBankId
     * @return
     */
    boolean saveDimensionStrategy(List<MbDimensionsDto> dimensions, Long questionBankId);
}

我们目前有三个类,都需求完结这个接口,来进行不同的行为,在这儿就是对维度的不同操作。

无维度

由于无维度无需处理,直接回来true即可

typescript拷贝代码@Component
public class SaveNoDimensionStrategy implements SaveDimensionStrategy {
    @Override
    public Integer getType() {
        return QuestionBankSettingTypeEnum.无维度.getValue();
    }
    @Override
    public boolean saveDimensionStrategy(List<MbDimensionsDto> dimensions, Long questionBankId) {
        return true;
    }
}

一级维度

typescript拷贝代码@Component
public class SaveOneDimensionStrategy implements SaveDimensionStrategy {
    @Resource
    private MbDimensionService mbDimensionService;
    @Override
    public Integer getType() {
        return QuestionBankSettingTypeEnum.一极维度.getValue();
    }
    @Override
    public boolean saveDimensionStrategy(List<MbDimensionsDto> dimensions, Long questionBankId) {
        checkIsDimensionsDtoLegal(dimensions);
        //批量增加
        dimensions.forEach(mbDimensionDto -> {
            MbDimension mbDimension = new MbDimension();
            BeanUtils.copyProperties(mbDimensionDto, mbDimension);
            mbDimension.setQuestionBankId(questionBankId);
            mbDimensionService.save(mbDimension);
        });
        return true;
    }
    private void checkIsDimensionsDtoLegal(List<MbDimensionsDto> dimensions) {
        dimensions.forEach(dimension -> {
            if (StringUtils.isEmpty(dimension.getName())) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR);
            }
            if (dimension.getWeight() < 0) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR);
            }
            List<MbDimensionsDto> children = dimension.getChildren();
            if (children != null && children.size() != 0) {
                //递归校验
                checkIsDimensionsDtoLegal(children);
            }
        });
    }
}

二级维度

scss拷贝代码@Component
public class SaveTwoDimensionStrategy implements SaveDimensionStrategy {
    @Resource
    private MbDimensionService mbDimensionService;
    @Override
    public Integer getType() {
        return QuestionBankSettingTypeEnum.二级维度.getValue();
    }
    @Override
    public boolean saveDimensionStrategy(List<MbDimensionsDto> dimensions, Long questionBankId) {
        checkIsDimensionsDtoLegal(dimensions);
        //新增树状dimensions
        dimensions.forEach(mbDimensionDto -> {
            MbDimension mbDimension = new MbDimension();
            BeanUtils.copyProperties(mbDimensionDto, mbDimension);
            mbDimension.setQuestionBankId(questionBankId);
            mbDimensionService.save(mbDimension);
            Long fatherId = mbDimension.getId();
            List<MbDimensionsDto> children = mbDimensionDto.getChildren();
            if(children == null){
                throw new BusinessException(ErrorCode.PARAMS_ERROR,"二级维度中,父维度一定要有子维度");
            }
            children.forEach(son -> {
                MbDimension mbDimensionSon = new MbDimension();
                BeanUtils.copyProperties(son, mbDimensionSon);
                mbDimensionSon.setQuestionBankId(questionBankId);
                //新增后的dimension的id,即为chidren dimension的父亲id.
                mbDimensionSon.setParentId(fatherId);
                mbDimensionService.save(mbDimensionSon);
            });
        });
        return true;
    }
    private void checkIsDimensionsDtoLegal(List<MbDimensionsDto> dimensions) {
        dimensions.forEach(dimension -> {
            if (StringUtils.isEmpty(dimension.getName())) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR);
            }
            if (dimension.getWeight() < 0) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR);
            }
            List<MbDimensionsDto> children = dimension.getChildren();
            if (children != null && children.size() != 0) {
                //递归校验
                checkIsDimensionsDtoLegal(children);
            }
        });
    }
}

由此可见,我们做的不过是把行为笼统成了接口,运用三个完结类去标准不同的行为。

这儿完结类都加上了@Component注解,让它变成一个个Bean,一起Spring去处理。

界说Map和runner

现在已经有了一系列处理计划,也就是我们写的一个个完结类。现在我们需求一个容器去把这一个个的处理计划跑起来!

见名知义,runner就是为此而生的。

我们界说一个
SaveDimensionStrategyRunner

java拷贝代码@Component
public interface SaveDimensionStrategyRunner {
    /**
     * 依据 type 字段执行不同战略
     * @return
     */
    boolean saveDimension(List<MbDimensionsDto> dimensionsDtos, Long questionBankId,Integer type);
}

接下来,我们界说完结类。

typescript拷贝代码/**
 * 用来处理战略方式运用的bean,放入map进行处理
 */
@Configuration
public class StrategyConfig {
    @Bean
    public SaveDimensionStrategyRunner strategyRunner(List<SaveDimensionStrategy> strategies) {
        Map<Integer, SaveDimensionStrategy> strategyMap = strategies.stream()
                .collect(Collectors.toMap(SaveDimensionStrategy::getType, s -> s));
        return (dimensionsDtoList,questionBankId,type) -> strategyMap.get(type).saveDimensionStrategy(dimensionsDtoList,questionBankId);
    }
}

这段代码做了几件作业:

  1. 由于之前界说的strategy被打上了@Component注解,因而,他们会被spring处理,注入到这个方法的参数里。
  2. 依据传入的各个strategy,我们将他们的type设置为key,自身设置为value,放入map调集中。
  3. return语句中是 对SaveDimensionStrategyRunner这个接口中的方法的完结,也就是说,我们回来的就是刚刚界说的接口的完结类,又由于加上了@Bean注解,该实例会被spring所处理。

运用

我们在需求这个方法的完结类中注入runner

java拷贝代码    @Resource
    private SaveDimensionStrategyRunner saveDimensionStrategyRunner;

然后直接调用它的方法就可以啦!