设计模式优化

128 阅读2分钟

业务背景

培训系统中的考试领域,在考生开始考试时,会根据当前试卷的状态来执行不同的业务处理。这其实是一个典型的工厂模式样例。

优化前

使用大量的if else来区分不同状态的业务。

if (status = 可以考试){
    执行答卷初始化;
    生成防止答卷超时未提交的定时任务;
}else if (status = 已有答卷在考试中){
    返回前台已经开始的答卷内容;
}else if (status = 未完成学习任务){
    返回前台继续完成学习任务的提示;
}

优化后

定义一个考卷对象

ExamAnswerDTO,用于返回给前台具体的考卷内容,具体属性就不展示了。

定义考卷状态的枚举

msg定义了当前状态的详情,在一些状态下也可用于对前台展示的提示。

beanName是每一个状态所对应的执行业务的beanName,这个设计模式能正确执行的关键点。

ExamAnswerStatus
START_EXAM(0,"开始考试","startExamBean"),
EXIST_EXAM(1,"已有答卷在考试中","existExamBean"),
NO_FINISH(2,"还有未完成的学习任务","noFinishBean");

private final Integer code;
private final String msg;
private final String beanName;

创建工厂和实现

// 工厂
public interface ExamAnswerInterface {
    // ExamAnswerDTO 中为考卷内容
    Result<ExamAnswerDTO> examAnswer(ExamAnswerReq req);
}

// 开始考试的实现
@Service
public StartExamBean implements ExamAnswerInterface{
    @Override
    public Result<ExamAnswerDTO> examAnswer(ExamAnswerReq req){
        // 初始化答卷,并将答卷内容返回
    	// 生成防止答卷超时未提交的定时任务
    }   
}

// 已有答卷在考试中
@Service
public ExistExamBean implements ExamAnswerInterface{
    @Override
    public Result<ExamAnswerDTO> examAnswer(ExamAnswerReq req){
        // 查询已有答卷内容,并返回
    }   
}

// 还有未完成的学习任务
@Service
public NoFinishBean implements ExamAnswerInterface{
    @Override
    public Result<ExamAnswerDTO> examAnswer(ExamAnswerReq req){
        return Result.createFaild("还有未完成的学习任务");
    }   
}

开始考试

public Result<ExamAnswerDTO> examAnswer(Request request){
    try{
        // 分布式锁防止重复考试
        commmentLock.tryLock("startExamLock" + examId);
        ExamAnswerStatus status = 另外的方法计算是否可以开始考试;
        // 从容器中获取bean
        ExamAnswerInterface examAnswerInterface = applicationContextComponent
            .getApplicationContext()
            .getBean(judgeExamResult.getBeanName(), ExamResultInterface.class);
        Result<ExamAnswerDTO> examAnswer = examAnswerInterface.examAnswer();
        // 做返回处理
    }catch (Exception e){
        // 处理异常
    }finally {
        commmentLock.unlock();
    }
}

获取bean

因为各自的工厂实现中还会用到其它的bean(例如查询等动作),所以这里的实现类如果能使用spring容器中的bean,就可以用自动注入的方式来管理用到的其它bean,会方便很多。

可以借助ApplicationContextAware,来实现获取bean,在spring容器初始化的时候,就会执行setApplicationContext保存上下文。

@Component
public class ApplicationContextComponent implements ApplicationContextAware {
    private static ApplicationContext context;

    public ApplicationContext getApplicationContext() {
        return context;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        context = applicationContext;
    }
}