运用战略方式处理多条件问题
问题重现
这是公司代码里边的一个接口,我需求依据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);
}
}
这段代码做了几件作业:
- 由于之前界说的strategy被打上了@Component注解,因而,他们会被spring处理,注入到这个方法的参数里。
- 依据传入的各个strategy,我们将他们的type设置为key,自身设置为value,放入map调集中。
- return语句中是 对SaveDimensionStrategyRunner这个接口中的方法的完结,也就是说,我们回来的就是刚刚界说的接口的完结类,又由于加上了@Bean注解,该实例会被spring所处理。
运用
我们在需求这个方法的完结类中注入runner
java拷贝代码 @Resource
private SaveDimensionStrategyRunner saveDimensionStrategyRunner;
然后直接调用它的方法就可以啦!