前言:在我们项目开发中呢基础会遇到一些多项选择的需求,在处理此类问题的时候采取多种设计模式对应解决是个不错的选择,能降低重复代码量,减少硬编码写死new 实例的情况,也提高代码可维护性。在这里我聊聊我一个项目中所使用的设计模式。
一、设计模式:
1.工厂模式
场景为我们在保证项目服务调用灵活性,也就是让项目能灵活切换代码沙箱种类,因为我们是通过三个具体接口实现类实现三种代码沙箱方式,接口调用,但是new实例的时候是需要更换代码沙箱类的,此时我们不可能用硬编码写死,所以引入工厂模式。
public class CodeSandboxFactory {
/**
* 工厂模式创建代码沙箱示例
*/
public static CodeSandbox newInstance(String type){
switch (type){
case "example": return new ExampleCodeSandbox();
case "remote": return new RemoteCodeSandbox();
case "ThirdParty": return new ThirdPartyCodeSandbox();
default: return new ExampleCodeSandbox();
}
}
}
/**
*测试工厂模式是否成功
* @param args
*/
public static void main(String[] args) {
Scanner myScanner = new Scanner(System.in);
while(myScanner.hasNext()){
String type = myScanner.next();
CodeSandbox codeSandbox = CodeSandboxFactory.newInstance(type);
String code = "int main(){}";
String language = QuestionSubmitLanguageEnum.JAVA.getValue();
List<String> inputList = Arrays.asList("1 2","3 4");
ExecuteCodeRequest executeCodeRequest = new ExecuteCodeRequest().builder()
.code(code)
.inputList(inputList)
.language(language)
.build();
codeSandbox.executeCode(executeCodeRequest);
}
}
但是也是依靠type这个字符串确定具体实例,所以我们要把这个type配置为在配置文件中可以通过配置方法更改type,也就用@Value注解注入数据,把type参数配置化;
# 代码沙箱配置
codesandbox:
type: example
@Value("${codesandbox.type:example}")
private String type;
2、代理模式
首先是场景,我们在对于调用代码沙箱,我们需要打印调用代码沙箱前后的日志参数,当然aop实现日志记录也是一个思路,不过用代理模式能实现更加细化灵活,解释就是用代理的方式调用这个接口,在调用接口前后进行统一日志操作,这样无论你使用哪个代码沙箱示例都能打印日志,不需要在每一个具体实现类中编写日志代码,降低代码重复,方便维护。
/**
* 代理模式实现代码沙箱增强
*/
@Slf4j
public class CodeSandboxProxy implements CodeSandbox{
private CodeSandbox codeSandbox;
public CodeSandboxProxy(CodeSandbox codeSandbox){
this.codeSandbox = codeSandbox;
}
@Override
public ExecuteResponse executeCode(ExecuteCodeRequest executeCodeRequest) {
log.info("代码沙箱请求信息"+executeCodeRequest.toString());
ExecuteResponse executeResponse = codeSandbox.executeCode(executeCodeRequest);
log.info("代码沙箱响应信息"+executeResponse.toString());
return executeResponse;
}
}
CodeSandbox codeSandbox = CodeSandboxFactory.newInstance(type);
codeSandbox = new CodeSandboxProxy(codeSandbox);
3、策略模式
在oj项目中,因为要实现多种语言的判题,且因为不同语言可能需要不同的判题策略,所以需要对应多个判题策略,所以我们采用策略模式实现多种判题策略来满足需求。
(1)策略模式需要一个信息上下文来存储用户提交代码的题的信息包括如下:(用于在策略中传递参数)
/**
* 上下文(用于定义在策略中传递的参数)
*/
@Data
public class JudgeContext {
private JudgeInfo judgeInfo;
private List<String> inputList;
private List<String> outputList;
private List<JudgeCase> judgeCaseList;
private Question question;
private QuestionSubmit questionSubmit;
}
(2)判题策略接口
/**
* 判题策略
*/
public interface JudgeStrategy {
/**
* 执行判题
* @param judgeContext
* @return
*/
JudgeInfo doJudge(JudgeContext judgeContext);
}
(3)判题管理类,也就是策略选择类,包含选择策略的逻辑,如提交信息中的参数language为java,就确定策略实例为 java策略实例;
/**
* 判题管理(简化调用)
*/
@Service
public class JudgeManager {
/**
* 执行判题
*
* @param judgeContext
* @return
*/
JudgeInfo doJudge(JudgeContext judgeContext) {
QuestionSubmit questionSubmit = judgeContext.getQuestionSubmit();
String language = questionSubmit.getLanguage();
JudgeStrategy judgeStrategy = new DefaultJudgeStrategy();
if ("java".equals(language)) {
judgeStrategy = new JavaLanguageJudgeStrategy();//这里是new Java策略实例
}
return judgeStrategy.doJudge(judgeContext);//调用的是确定实例的策略方法
}
}
(4)在(3)之前,把具体各个语言的判题策略具体实现就好了;