使用工厂+策略模式实现去除繁琐的if else
使用工厂+策略模式实现去除繁琐的if else
在中间有一个mapstruct的bug,即在修改实体类中的类型时,或者修改属性名字,mapstruct都无法进行转换,会报错,此时需要maven clean+maven compile即可
前言
在这次的开发中,有一个增加题目的需求,其中题目中有SubjectType对应4种不同的类型,单选多选判断简答。在增加题目的接口中,如果对每个都if一遍,十分繁琐,也不利于后期的扩展,于是选择交给工厂方法去处理,每一个类型的题目有自己的策略类型,然后通过工厂进行创建
创建枚举类型
对应的枚举类型,目的是方便通过传入的Type(值是1234对应四种不同的题型)
并且写出方法根据code找出枚举类
package com.gy.subject.common.enums;
public enum SubjectTypeEnum {
Radio(1,"单选"),
Multiple(2,"多选"),
Judge(3,"判断"),
Brief(4,"简答");
private int code;
private String desc;
SubjectTypeEnum(int code,String desc){
this.code = code;
this.desc = desc;
}
public static SubjectTypeEnum getByCode(int code){
for(SubjectTypeEnum x : SubjectTypeEnum.values()){
if(x.code == code){
return x;
}
}
return null;
}
}
创建策略类接口
策略类即却确定了是这个类型,里面包含了具体业务逻辑,比如确定了是单选题之后,那么就要增加一道单选题目,以及后续的业务(此处是把题目对应的标签及分类也增加上)其实后续的业务也可以不在此处添加,让策略类只专注于对于的题型处理
定义了每个具体的策略类可以做获取具体的枚举类,用于后面根据type找出,以及具体的业务增加代码类
package com.gy.subject.domain.handler;
import com.gy.subject.common.enums.SubjectTypeEnum;
import com.gy.subject.domain.entity.SubjectInfoBO;
import org.springframework.stereotype.Component;
/**
* @ClassName SubjectTypeHandeler
* @Description 题目处理器
* @Author gy
* @Date 2024/12/29
*/
@Component
public interface SubjectTypeHandeler {
/**
* @Description: 获取处理器类型
* @Param: []
* @return: com.gy.subject.common.enums.SubjectTypeEnum
* @Author: gy
* @Date: 2024/12/29
*/
SubjectTypeEnum getHandelerType();
/**
* @Description: 添加题目
* @Param: [subjectInfoBO]
* @return: void
* @Author: gy
* @Date: 2024/12/29
*/
void add(SubjectInfoBO subjectInfoBO);
}
具体的策略类
以创建一个单选为例,实现抽象策略类接口
package com.gy.subject.domain.handler;
import com.google.common.base.Preconditions;
import com.gy.subject.common.enums.SubjectTypeEnum;
import com.gy.subject.domain.convert.RadioSubjectConverter;
import com.gy.subject.domain.entity.SubjectInfoBO;
import com.gy.subject.infra.basic.entity.SubjectMapping;
import com.gy.subject.infra.basic.entity.SubjectRadio;
import com.gy.subject.infra.basic.service.SubjectMappingService;
import com.gy.subject.infra.basic.service.SubjectRadioService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.LinkedList;
import java.util.List;
/**
* @ClassName RadioTypeHandler
* @Description 单选的处理器
* @Author gy
* @Date 2024/12/29
*/
@Component
public class RadioTypeHandler implements SubjectTypeHandeler {
@Resource
private SubjectRadioService subjectRadioService;
@Resource
private SubjectMappingService subjectMappingService;
/**
* @Description: 获取单选处理器类型
* @Param: []
* @return: com.gy.subject.common.enums.SubjectTypeEnum
* @Author: gy
* @Date: 2024/12/29
*/
@Override
public SubjectTypeEnum getHandelerType() {
return SubjectTypeEnum.Radio;
}
/**
* @Description: 添加题目
* @Param: [subjectInfoBO]
* @return: void
* @Author: gy
* @Date: 2024/12/29
*/
@Override
public void add(SubjectInfoBO subjectInfoBO) {
List<SubjectRadio> radioList = new LinkedList<>();
//List<SubjectMapping> subjectMappingList = new LinkedList<>();
Preconditions.checkNotNull(radioList,"单选的四个答案list不能为空");
subjectInfoBO.getOptionList().forEach(option -> {
SubjectRadio radio = RadioSubjectConverter.INSTANCE.converterAnswerToRadio(option);
radio.setSubjectId(subjectInfoBO.getId());
radioList.add(radio);
});
subjectRadioService.batchInsert(radioList);
// subjectInfoBO.getCategoryIds().forEach(categoryId -> {
// subjectInfoBO.getLabelIds().forEach(labelId -> {
// SubjectMapping subjectMapping = new SubjectMapping();
// subjectMapping.setSubjectId(subjectInfoBO.getId());
// subjectMapping.setCategoryId(Long.valueOf(categoryId));
// subjectMapping.setLabelId(Long.valueOf(labelId));
// subjectMappingList.add(subjectMapping);
// });
// });
//
// subjectMappingService.batchInsert(subjectMappingList);
}
}
接下来可以创建不同的题型,如若想要扩展题型,那么只需要增加一个枚举类
创建工厂类
工厂类中从bean工厂中找出题型的策略类组成一个list,目的是注入到map中,方便根据type从map中直接找出具体策略类。
其实不用map也可以,直接遍历一次,通过enum的value.code 进行对比,使用map技术层面来说更好
package com.gy.subject.domain.handler;
import com.gy.subject.common.enums.SubjectTypeEnum;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
*
* 题目类型工厂
* @author 高悦
* @version 1.0
* @description: TODO
* @date 2024/12/29 17:09
*/
@Component
public class SubjectTypeHandlerFactory implements InitializingBean {
@Resource
private List<SubjectTypeHandeler> subjectTypeHandelerList;
private Map<SubjectTypeEnum,SubjectTypeHandeler> subjectTypeHandelerMap = new HashMap<>();
public SubjectTypeHandeler getSubjectTypeHandler(int SubjectType){
SubjectTypeEnum subjectTypeEnum = SubjectTypeEnum.getByCode(SubjectType);
return subjectTypeHandelerMap.get(subjectTypeEnum);
}
@Override
public void afterPropertiesSet() throws Exception {
for(SubjectTypeHandeler x : subjectTypeHandelerList){
subjectTypeHandelerMap.put(x.getHandelerType(),x);
}
}
}
业务层
业务层中就可以解放了,工厂直接根据type选择到具体的策略然后执行业务
//上一个工厂加策略的形式
//一个工厂 包含了4种类型,根据传入的type自动映射选择处理
//可以节省一大堆的if(因为题目信息里面要有选择是单选还是多选)
SubjectInfo subjectInfo = SubjectInfoBOConverter.INSTANCE.SubjectInfoBOtoInfo(subjectInfoBO);
SubjectInfo insert = subjectInfoService.insert(subjectInfo);
subjectInfoBO.setId(insert.getId());
SubjectTypeHandeler subjectTypeHandler = subjectTypeHandlerFactory.getSubjectTypeHandler(subjectInfoBO.getSubjectType());
subjectTypeHandler.add(subjectInfoBO);