需求
业务需求是,有一个代报考系统,里面的一个功能是根据报考类目的不同维护不同的代报考规则。
代报考规则的实体:
public class ExamRuleEditReqDTO extends BaseDTO {
// 无 则新增 有则 修改
private Integer id;
//报考规则名称
@NotNull(message = "报考规则名称不能为空!")
private String name;
/**
* 报考类目 typeCode
* 消防工程师 HC_EXAM_TYPE_FIRE_ENGINEER
* 成人高考 HC_EXAM_TYPE_ADULT_EXAM
* ACI 心理 HC_EXAM_TYPE_ACI_PSYCHOLOGY
* ACI 营养 HC_EXAM_TYPE_ACI_NUTRITION
* 健康管理师 HC_EXAM_TYPE_HEALTH_MANAGER
* 自定义类目 HC_EXAM_TYPE_CUSTOMIZE
*/
@NotNull(message = "报考类目Code不能为空!")
private String typeCode;
//是否免协报费
private String exemptionAssistFlag;
// ... 省略 get / set ...
}
service接口
public interface ExamService {
//维护代报考规则
ResponseBaseDTO<Integer> editExamRule(ExamRuleEditReqDTO reqDTO);
//查询代报考规则
ResponseBaseDTO<ExamRuleDetailQueryRespDTO> queryExamRuleDetail(ExamRuleQueryReqDTO reqDTO);
}
传统实现
根据报考类目写一堆的if else
:
@Service
public class ExamServiceImpl implements ExamService {
@Autowired
private ExamRuleFireEngineerHandler examRuleFireEngineerHandler;
@Autowired
private ExamRuleAdultExamHandler examRuleAdultExamHandler;
@Autowired
private ExamRuleAciPsychologyHandler examRuleAciPsychologyHandler;
@Autowired
private ExamRuleAciNutritionHandler examRuleAciNutritionHandler;
@Autowired
private ExamRuleHealthManagerHandler examRuleHealthManagerHandler;
@Autowired
private ExamRuleCustomizeHandler examRuleCustomizeHandler;
public ResponseBaseDTO<Integer> editExamRule(ExamRuleEditReqDTO reqDTO) {
if (ExamConstants.HC_EXAM_TYPE_FIRE_ENGINEER.equals(reqDTO.getTypeCode())) {
return examRuleFireEngineerHandler.editHandler(reqDTO);
} else if (ExamConstants.HC_EXAM_TYPE_ADULT_EXAM.equals(reqDTO.getTypeCode())) {
return examRuleAdultExamHandler.editHandler(reqDTO);
} else if (ExamConstants.HC_EXAM_TYPE_ACI_PSYCHOLOGY.equals(reqDTO.getTypeCode())) {
return examRuleAciPsychologyHandler.editHandler(reqDTO);
} else if (ExamConstants.HC_EXAM_TYPE_ACI_NUTRITION.equals(reqDTO.getTypeCode())) {
return examRuleAciNutritionHandler.editHandler(reqDTO);
} else if (ExamConstants.HC_EXAM_TYPE_HEALTH_MANAGER.equals(reqDTO.getTypeCode())) {
return examRuleHealthManagerHandler.editHandler(reqDTO);
}
return new ResponseBaseDTO<>(ResponseBaseDTO.FLAG_FAIL, "代报考类目参数不正确!");
}
public ResponseBaseDTO<ExamRuleDetailQueryRespDTO> queryExamRuleDetail(ExamRuleQueryReqDTO reqDTO) {
if (ExamConstants.HC_EXAM_TYPE_FIRE_ENGINEER.equals(reqDTO.getTypeCode())) {
return examRuleFireEngineerHandler.editHandler(reqDTO);
} else if (ExamConstants.HC_EXAM_TYPE_ADULT_EXAM.equals(reqDTO.getTypeCode())) {
return examRuleAdultExamHandler.editHandler(reqDTO);
} else if (ExamConstants.HC_EXAM_TYPE_ACI_PSYCHOLOGY.equals(reqDTO.getTypeCode())) {
return examRuleAciPsychologyHandler.editHandler(reqDTO);
} else if (ExamConstants.HC_EXAM_TYPE_ACI_NUTRITION.equals(reqDTO.getTypeCode())) {
return examRuleAciNutritionHandler.editHandler(reqDTO);
} else if (ExamConstants.HC_EXAM_TYPE_HEALTH_MANAGER.equals(reqDTO.getTypeCode())) {
return examRuleHealthManagerHandler.editHandler(reqDTO);
} else if (ExamConstants.HC_EXAM_TYPE_CUSTOMIZE.equals(reqDTO.getTypeCode())) {
return examRuleHealthManagerHandler.editHandler(reqDTO);
}
return new ResponseBaseDTO<>(ResponseBaseDTO.FLAG_FAIL, "代报考类目参数不正确!");
}
}
策略模式实现
利用策略模式,只需要两行即可实现业务逻辑:
@Service
public class ExamServiceImpl implements ExamService {
@Autowired
private ExamRuleHandlerContext examRuleHandlerContext;
@Override
@Transactional
public ResponseBaseDTO<Integer> editExamRule(ExamRuleEditReqDTO reqDTO ) {
return this.examRuleHandlerContext.getHandlerInstance(reqDTO.getTypeCode()).editHandler(reqDTO );
}
@Override
public ResponseBaseDTO<ExamRuleDetailQueryRespDTO> queryExamRuleDetail(ExamRuleQueryReqDTO reqDTO) {
return this.examRuleHandlerContext.getHandlerInstance(reqDTO.getTypeCode()).queryHandler(reqDTO);
}
}
可以看到上面的方法中注入了ExamRuleHandlerContext,这是一个处理器上下文,用来保存不同的业务处理器,具体在下文会讲解。我们从中获取一个抽象的处理器AbstractExamRuleHandler,调用其方法实现业务逻辑。
现在可以了解到,我们主要的业务逻辑是在处理器中实现的,因此有多少个代报考类目,就对应有多少个处理器。以后需求变化,增加了代报考类目,只需要添加相应的处理器就可以,上述ExamServiceImpl完全不需改动。
业务处理器的写法
@Component
@ExamRuleHandler(ExamConstants.HC_EXAM_TYPE_CUSTOMIZE)
public class ExamRuleCustomizeHandler extends AbstractExamRuleHandler {
@Override
public ResponseBaseDTO<Integer> editHandler(ExamRuleEditReqDTO reqDTO){
//处理自定义类目的规则
return null;
}
}
@Component
@ExamRuleHandler(ExamConstants.HC_EXAM_TYPE_HEALTH_MANAGER)
public class ExamRuleCustomizeHandler extends AbstractExamRuleHandler {
@Override
public ResponseBaseDTO<Integer> editHandler(ExamRuleEditReqDTO reqDTO){
//处理健康管理师类目的规则
return null;
}
}
@Component
@ExamRuleHandler(ExamConstants.HC_EXAM_TYPE_ACI_NUTRITION)
public class ExamRuleCustomizeHandler extends AbstractExamRuleHandler {
@Override
public ResponseBaseDTO<Integer> editHandler(ExamRuleEditReqDTO reqDTO){
//处理ACI营养类目的规则
return null;
}
}
@Component
@ExamRuleHandler(ExamConstants.HC_EXAM_TYPE_ACI_PSYCHOLOGY)
public class ExamRuleCustomizeHandler extends AbstractExamRuleHandler {
@Override
public ResponseBaseDTO<Integer> editHandler(ExamRuleEditReqDTO reqDTO){
//处理ACI心理类目的规则
return null;
}
}
@Component
@ExamRuleHandler(ExamConstants.HC_EXAM_TYPE_ADULT_EXAM)
public class ExamRuleCustomizeHandler extends AbstractExamRuleHandler {
@Override
public ResponseBaseDTO<Integer> editHandler(ExamRuleEditReqDTO reqDTO){
//处理成人高考类目的规则
return null;
}
}
@Component
@ExamRuleHandler(ExamConstants.HC_EXAM_TYPE_FIRE_ENGINEER)
public class ExamRuleCustomizeHandler extends AbstractExamRuleHandler {
@Override
public ResponseBaseDTO<Integer> editHandler(ExamRuleEditReqDTO reqDTO){
//处理消防工程师类目的规则
return null;
}
}
首先每个处理器都必须添加到spring
容器中,因此需要加上@Component
注解,其次需要加上一个自定义注解@ExamRuleHandler
,用于标识该处理器对应哪个订单类型,最后就是继承AbstractExamRuleHandler,实现自己的业务逻辑。
自定义注解 @HandlerType
import java.lang.annotation.*;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface ExamRuleHandler {
String value();
}
抽象处理器 AbstractHandler
public abstract class AbstractExamRuleHandler {
public abstract ResponseBaseDTO<Integer> editHandler(ExamRuleEditReqDTO reqDTO );
public abstract ResponseBaseDTO<ExamRuleDetailQueryRespDTO> queryHandler(ExamRuleQueryReqDTO reqDTO );
}
自定义注解和抽象处理器都很简单,那么如何将处理器注册到spring
容器中呢?
具体思路是:
- 扫描指定包中标有
@ExamRuleHandler
的类; - 将注解中的类型值作为
key
,对应的类作为value
,保存在Map
中; - 重写 实现ApplicationContextAware接口的ExamRuleHandlerContext类中setApplicationContext方法,当spring容器初始化的时候,会自动的将ApplicationContext注入进来
我们将核心的功能封装在ExamRuleHandlerContext类中,完成上面的功能。
上下文处理器ExamRuleHandlerContext
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
@Service
public class ExamRuleHandlerContext implements ApplicationContextAware {
@Autowired
ApplicationContext applicationContext;
private static final Map<String,Class> handlerMap = new HashMap<>(10);
public AbstractExamRuleHandler getHandlerInstance(String typeCode){
Class clazz = handlerMap.get(typeCode);
return (AbstractExamRuleHandler) applicationContext.getBean(clazz);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
Map<String,Object> beans = applicationContext.getBeansWithAnnotation(ExamRuleHandler.class);
if (beans != null && beans.size() > 0) {
for (Object serviceBean : beans.values()) {
String payType = serviceBean.getClass().getAnnotation(ExamRuleHandler.class).value();
handlerMap.put(payType, serviceBean.getClass());
}
}
}
}
#getHandlerInstance方法根据类型获取对应的class,然后根据class类型获取注册到
spring`中的bean。
最后请注意一点,ExamRuleHandlerContext 必须能被扫描到,或者通过
@Bean
的方式显式的注册,才能在项目启动时发挥作用。
总结
利用策略模式可以简化繁杂的if else
代码,方便维护,而利用自定义注解和自注册的方式,可以方便应对需求的变更。本文只是提供一个大致的思路,还有很多细节可以灵活变化,例如使用枚举类型、或者静态常量,作为代报考的类型,相信你能想到更多更好的方法。
本文由 小马哥 创作,采用 知识共享署名4.0 国际许可协议进行许可
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名
最后编辑时间为:
2020/11/14 17:25