一、项目背景
最近自己在做项目开发的时候,遇到了这样的场景:我需要添加题目到数据库,题目的类型分为判断题、多选题、单选题等,并且每一个题目类型对应建有数据库,那么抛开其他表添加数据的需求,最核心的业务就是前端回来一堆数据中,包含一个题目类型的type字段,后端的业务就是根据类型去对应的题目类型数据库进行添加操作。
那么按照传统的做法,我们肯定在service里面去判断type类型,然后判断题调用判断题的service、多选题调用多选题的service,这种设计后期会导致一些问题:
1、大量重复的IF嵌套导致屎山代码,如果表多自己都写晕
2、不便于后期维护,后期添加或修改功能会直接改动源代码
此时我们使用策略 + 工厂模式可以完美的解决上述的一系列问题,那么什么是策略模式?什么又是工厂模式?下面为大家简单介绍了一下。
二、策略模式简介
策略模式:
策略模式自定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理。
UML图:
三、工厂模式简介
工厂模式主要分为三种类型:简单工厂模式、工厂方法模式和抽象工厂模式。这里我们主要介绍工厂方法模式
工厂方法模式:
工厂方法模式,又称工厂模式、多态工厂模式和虚拟构造器模式,通过定义工厂父类负责定义创建对象的公共接口,而子类则负责生成具体的对象。简单来说,工厂方法模式就是把具体产品的创建推迟到工厂类的子类(具体工厂)中,此时工厂类不再负责所有产品的创建,而只是给出具体工厂必须实现的接口,这样工厂方法模式在添加新产品的时候就不修改工厂类逻辑而是添加新的工厂子类,符合开放封闭原则
四、策略+工厂模式的项目应用
首先我们创建一个题目类型的策略接口
public interface SubjectTypeHandler {
/**
* 枚举身份的识别
*/
SubjectInfoEnum getHandlerType();/**
* 实际题目的插入
* @param subjectInfoBo
*/
void add(SubjectInfoBo subjectInfoBo);
}
创建题目类型的工厂,并且定义好getHandler方法接收外部传回来的题目类型,然后封装成一个SubjectTypeHandler类型返回
/**
* 题目类型工厂
*/
@Component
public class SubjectTypeHandlerFactory implements InitializingBean {@Resource
private List<SubjectTypeHandler> subjectTypeHandlers;private Map<SubjectInfoEnum, SubjectTypeHandler> handlerMap = new HashMap<>();
/**
* getHandler方法接收外部传来的题目类型,并封装成一个题目策略(handler)类型返回
*/
public SubjectTypeHandler getHandler(Integer subjectType) {
SubjectInfoEnum subjectInfoEnum = SubjectInfoEnum.getByCode(subjectType);
return handlerMap.get(subjectInfoEnum);
}@Override
public void afterPropertiesSet() throws Exception {
for (SubjectTypeHandler subjectTypeHandler : subjectTypeHandlers) {
handlerMap.put(subjectTypeHandler.getHandlerType(), subjectTypeHandler);
}
}
}
然后我们依次创建好如:简答题、判断题、多选题等题目策略并实现题目类型的策略,并且重写add 方法,然后在各自的add方法中完成对各自数据库的交互操作,这里以判断题、简答题题目策略为例。
/** * 简答题目的策略 */ @Component public class BriefTypeHandler implements SubjectTypeHandler{ @Resource private SubjectBriefService briefService; @Override public SubjectInfoEnum getHandlerType() { return SubjectInfoEnum.BRIEF; } @Override public void add(SubjectInfoBo subjectInfoBo) { //简答题目的插入 SubjectBrief subjectBrief = BriefSubjectConvert.INSTANCE.RadioBoToLabel(subjectInfoBo); subjectBrief.setId(subjectInfoBo.getId()); subjectBrief.setIsDeleted(IsDeleteFlagEnum.UN_DELETE.getCode()); briefService.insert(subjectBrief); }}
/** * 判断题目的策略 */ @Component public class JudgeTypeHandler implements SubjectTypeHandler{ @Resource private SubjectJudgeService subjectJudgeService; @Override public SubjectInfoEnum getHandlerType() { return SubjectInfoEnum.JUDGE; } @Override public void add(SubjectInfoBo subjectInfoBo) { //判断题目的插入 SubjectJudge subjectJudge = new SubjectJudge(); SubjectAnswerBo subjectAnswerBo = subjectInfoBo.getOptionList().get(0); subjectJudge.setSubjectId(subjectInfoBo.getId()); subjectJudge.setIsCorrect(subjectAnswerBo.getIsCorrect()); subjectJudgeService.insert(subjectJudge); }}
当创建好这写策略+工厂类后,我们就可以在项目中去调用方法进行操作
SubjectTypeHandler subjectTypeHandler =
subjectTypeHandlerFactory.getHandler(subjectInfo.getSubjectType());
我们通过题目类型工厂,将前端传来的题目类型传入的题目类型进行封装,封装为subjectTypeHandler策略类型,然后我们通过这个subjectTypeHandler可以直接调用add方法,此时题目策略每个字段封装了题目类型识别,所以会直接进入subjectInfo.getSubjectType()所对应的题目类型策略
subjectTypeHandler.add(subjectInfoBo);
注: subjectInfoBo是由前端传入进来,而subjectInfo是由subjectInfoBo转换而来,详情见下图
service调用handler完整代码为
在使用工厂+策略模式后,当我们是需要添加新的题型时,无需再改动源代码,而是直接添加新的策略类或新的策略方法,如下图,并重写对应方法,符合开闭原则,大大提高代码的美观程度,降低维护成本。
新增查询功能只需添加策略方法并在子类对应重写即可!小伙伴们快去用起来吧!