OJ在线评测系统 后端 用策略模式优化判题机架构

判题机架构优化(策略模式)

思考

我们的判题策略可能会有很多种 比如 我们的代码沙箱本身执行程序需要消耗时间

这个时间可能不同的编程语言是不同的 比如沙箱执行Java要额外花费2秒

我们可以采用策略模式 针对不同的情况 定义不同独立的策略

而不是把所有情况全部放在一个if else里面

定义一个策略接口

我們先写一个方法

这个方法传入的是一个上下文对象context

package com.dduo.dduoj.judge.strategy;import com.dduo.dduoj.model.dto.questionsubmit.JudgeInfo;/*
* 判题策略
* */
public interface JudgeStrategy {/** 执行判题* @param judgeContext* @return* */JudgeInfo doJudge(JudgeContext judgeContext);}

创建这个上下文对象

我们传入的数据 有判题状态 输入用例 输出用例

用于定义在策略中传递的参数

package com.dduo.dduoj.judge.strategy;import com.dduo.dduoj.model.dto.questionsubmit.JudgeInfo;
import com.dduo.dduoj.model.entity.Question;
import lombok.Data;import java.util.List;/*
* 上下文 用于定义在策略中传递的参数
* */@Data
public class JudgeContext {private JudgeInfo judgeInfo;private List<String> inputList;private List<String> outputList;private Question question;}

接下来我们要把刚刚写的判题逻辑部分的代码搬到接口的实现类里面去

这边创建一个策略模式接口的实现类

默认实现类

类名为DefaultJudgeStrategy

我们先从上下文对象中获得信息

再进行判断

返回值

package com.dduo.dduoj.judge.strategy;import cn.hutool.json.JSONUtil;
import com.dduo.dduoj.model.dto.question.JudgeCase;
import com.dduo.dduoj.model.dto.question.JudgeConfig;
import com.dduo.dduoj.model.dto.questionsubmit.JudgeInfo;
import com.dduo.dduoj.model.entity.Question;
import com.dduo.dduoj.model.enums.JudgeInfoMessageEnum;import javax.swing.*;
import java.util.List;public class DefaultJudgeStrategyImpl implements JudgeStrategy {/*** 执行判题* @param judgeContext* @return*/@Overridepublic JudgeInfo doJudge(JudgeContext judgeContext) {// 从上下文对象获取信息JudgeInfo judgeInfo = judgeContext.getJudgeInfo();List<String> inputList = judgeContext.getInputList();List<String> outputList = judgeContext.getOutputList();Question question = judgeContext.getQuestion();List<JudgeCase> judgeCaseList = judgeContext.getJudgeCaselist();// 从判题信息中获取信息Long memory = judgeInfo.getMemoryLimit();Long time = judgeInfo.getTime();JudgeInfo judgeInfoResponse = new JudgeInfo();JudgeInfoMessageEnum judgeInfoMessageEnum = JudgeInfoMessageEnum.Accepted;judgeInfoResponse.setMemoryLimit(memory);judgeInfoResponse.setTime(time);// 先判断沙箱执行的结果输出数量是否和预期输出数量相等if (outputList.size() != inputList.size()) {judgeInfoMessageEnum = JudgeInfoMessageEnum.Wrong_Answer;judgeInfoResponse.setMessage(judgeInfoMessageEnum.getValue());return judgeInfoResponse;}// 依次判断每一项输出和预期输出是否相等for (int i = 0; i < judgeCaseList.size(); i++) {JudgeCase judgeCase = judgeCaseList.get(i);if (!judgeCase.getOutput().equals(outputList.get(i))) {judgeInfoMessageEnum = JudgeInfoMessageEnum.Wrong_Answer;judgeInfoResponse.setMessage(judgeInfoMessageEnum.getValue());return judgeInfoResponse;}}// 判断题目限制String judgeConfigStr = question.getJudgeConfig();JudgeConfig judgeConfig = JSONUtil.toBean(judgeConfigStr, JudgeConfig.class);Long needMemoryLimit = judgeConfig.getMemoryLimit();Long needTimeLimit = judgeConfig.getTimeLimit();if (memory > needMemoryLimit) {judgeInfoMessageEnum = JudgeInfoMessageEnum.Memory_Limit_Exceeded;judgeInfoResponse.setMessage(judgeInfoMessageEnum.getValue());return judgeInfoResponse;}if (time > needTimeLimit) {judgeInfoMessageEnum = JudgeInfoMessageEnum.Memory_Limit_Exceeded;judgeInfoResponse.setMessage(judgeInfoMessageEnum.getValue());return judgeInfoResponse;}judgeInfoResponse.setMessage(judgeInfoMessageEnum.getValue());return judgeInfoResponse;}
}

我们把所有判题的内容 放到一个单独的类里面 这个类叫做默认策略

接下来我们就可以在实现类里面用默认策略去解决

先根据沙箱的执行结果设置题目的判题状态和信息

再把上下文对象放到策略模式里面去

package com.dduo.dduoj.judge;import cn.hutool.json.JSONUtil;
import com.dduo.dduoj.common.ErrorCode;
import com.dduo.dduoj.exception.BusinessException;
import com.dduo.dduoj.judge.codesandbox.CodeSandbox;
import com.dduo.dduoj.judge.codesandbox.CodeSandboxFactory;
import com.dduo.dduoj.judge.codesandbox.CodeSandboxProxy;
import com.dduo.dduoj.judge.codesandbox.model.ExecuteCodeRequest;
import com.dduo.dduoj.judge.codesandbox.model.ExecuteCodeResponse;
import com.dduo.dduoj.judge.strategy.DefaultJudgeStrategy;
import com.dduo.dduoj.judge.strategy.JudgeContext;
import com.dduo.dduoj.judge.strategy.JudgeStrategy;
import com.dduo.dduoj.model.dto.question.JudgeCase;
import com.dduo.dduoj.model.dto.questionsubmit.JudgeInfo;
import com.dduo.dduoj.model.entity.Question;
import com.dduo.dduoj.model.entity.QuestionSubmit;
import com.dduo.dduoj.model.enums.QuestionSubmitStatusEnum;
import com.dduo.dduoj.service.QuestionService;
import com.dduo.dduoj.service.QuestionSubmitService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;import javax.annotation.Resource;
import java.util.List;
import java.util.stream.Collectors;@Service
public class JudgeServiceImpl implements JudgeService {// 题目服务@Resourceprivate QuestionService questionService;// 题目提交服务@Resourceprivate QuestionSubmitService questionSubmitService;@Value("${codesandbox.type:example}")private String value;@Overridepublic QuestionSubmit doJudge(Long questionSubmitId) {QuestionSubmit questionSubmit = questionSubmitService.getById(questionSubmitId);if (questionSubmit == null) {throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "提交信息不存在");}//拿到题目提交信息Long questionId = questionSubmit.getQuestionId();//拿到题目Question question = questionService.getById(questionId);if (question == null) {throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "题目不存在");}// 题目存在// 开始判题// 更改题目的状态 status// 如果不为等待状态if (questionSubmit.getStatus().equals(QuestionSubmitStatusEnum.WAITING.getValue())) {throw new BusinessException(ErrorCode.OPERATION_ERROR, "题目正在判题中");}// 重新设置QuestionSubmit questionSubmitUpdate = new QuestionSubmit();questionSubmitUpdate.setId(questionSubmitId);questionSubmitUpdate.setStatus(QuestionSubmitStatusEnum.RUNNING.getValue());boolean judge = questionSubmitService.updateById(questionSubmitUpdate);if (!judge) {throw new BusinessException(ErrorCode.SYSTEM_ERROR, "题目状态更新错误");}// 接下来放代码沙箱CodeSandbox codeSandbox = CodeSandboxFactory.NewInstance(value);codeSandbox = new CodeSandboxProxy(codeSandbox);// 拿出数据String code = questionSubmit.getCode();String language = questionSubmit.getLanguage();// 获取输入用例String judgeCaseStr = question.getJudgeCase();List<JudgeCase> judgeCaselist = JSONUtil.toList(judgeCaseStr, JudgeCase.class);List<String> inputList = judgeCaselist.stream().map(JudgeCase::getInput).collect(Collectors.toList());ExecuteCodeRequest executeRequest = ExecuteCodeRequest.builder().code(code).language(language).inputList(inputList).build();ExecuteCodeResponse executeCodeResponse=codeSandbox.executeCode(executeRequest);List<String> outputList = executeCodeResponse.getOutputList();// 根据沙箱的执行结果 设置题目的判题状态和信息JudgeContext judgeContext = new JudgeContext();judgeContext.setJudgeInfo(executeCodeResponse.getJudgeInfo());judgeContext.setInputList(inputList);judgeContext.setOutputList(outputList);judgeContext.setQuestion(question);judgeContext.setJudgeCaselist(judgeCaselist);// 用默认策略去解决JudgeStrategy judgeStrategy=new DefaultJudgeStrategy();JudgeInfo judgeInfo = judgeStrategy.doJudge(judgeContext);// 修改数据库中的判题结果questionSubmitUpdate = new QuestionSubmit();questionSubmitUpdate.setId(questionSubmitId);questionSubmitUpdate.setStatus(QuestionSubmitStatusEnum.SUCCESS.getValue());questionSubmitUpdate.setJudgeInfo(JSONUtil.toJsonStr(judgeInfo));// 看数据库boolean update = questionSubmitService.updateById(questionSubmitUpdate);if (!update) {throw new BusinessException(ErrorCode.SYSTEM_ERROR, "题目状态更新错误");}// 返回结果QuestionSubmit questionSubmitResult = questionSubmitService.getById(questionId);return questionSubmitResult;}
}

刚才只是定义了默认策略

我们还要定义其他策略

如Java程序的执行的策略

接下来我们就要去想一想如何去切换策略

但是如果用简单的判断 如果有复杂的情况会很麻烦 而且 写的if else语句会变的很多

建议单独编写一个判断策略的方法 或者是类

工厂 map缓存

定义JudgeManager 目的是尽量简化对判题功能的调用

让调用方最简便

去操作

补全代码

实际上就是对我们选择合适的策略这个过程

或者是进行其他的判断的时候

进行了一个判断

package com.dduo.dduoj.judge;import com.dduo.dduoj.judge.strategy.DefaultJudgeStrategy;
import com.dduo.dduoj.judge.strategy.JavaLanguageJudgeStrategy;
import com.dduo.dduoj.judge.strategy.JudgeContext;
import com.dduo.dduoj.judge.strategy.JudgeStrategy;
import com.dduo.dduoj.model.dto.questionsubmit.JudgeInfo;
import com.dduo.dduoj.model.entity.QuestionSubmit;/*
*  判题管理(简化调用)
* */
public class JudgeManger {/** 执行判题* @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();}return judgeStrategy.doJudge(judgeContext);}}

这样就行

在之前的题目提交实现类 核心逻辑里面

那么我们的核心逻辑就是这样的

@Override
public long doQuestionSubmit(QuestionSubmitAddRequest questionSubmitAddRequest, User loginUser) {// 校验编程语言是否合法String language = questionSubmitAddRequest.getLanguage();QuestionSubmitLanguageEnum languageEnum = QuestionSubmitLanguageEnum.getEnumByValue(language);if (languageEnum == null) {throw new BusinessException(ErrorCode.PARAMS_ERROR, "编程语言错误");}long questionId = questionSubmitAddRequest.getQuestionId();// 判断实体是否存在,根据类别获取实体Question question = questionService.getById(questionId);if (question == null) {throw new BusinessException(ErrorCode.NOT_FOUND_ERROR);}// 是否已提交题目long userId = loginUser.getId();// 每个用户串行提交题目QuestionSubmit questionSubmit = new QuestionSubmit();questionSubmit.setUserId(userId);questionSubmit.setQuestionId(questionId);questionSubmit.setCode(questionSubmitAddRequest.getCode());questionSubmit.setLanguage(language);// 设置初始状态questionSubmit.setStatus(QuestionSubmitStatusEnum.WAITING.getValue());questionSubmit.setJudgeInfo("{}");boolean save = this.save(questionSubmit);if (!save){throw new BusinessException(ErrorCode.SYSTEM_ERROR, "数据插入失败");}// todo 执行判题服务Long questionSubmitId = questionSubmit.getId();// 执行判题服务CompletableFuture.runAsync(() -> {judgeService.doJudge(questionSubmitId);});return questionSubmitId;
}

这边懒加载

可以解决循环依赖

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1547310.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

二分图算法模板以及简单应用

染色法判断二分图 给定一个 n 个点 m 条边的无向图&#xff0c;图中可能存在重边和自环。 请你判断这个图是否是二分图。 输入格式 第一行包含两个整数 n 和 m。 接下来 m 行&#xff0c;每行包含两个整数 u 和 v&#xff0c;表示点 u 和点 v 之间存在一条边。 输出格式 …

Matplotlib | 一文搞定Matplotlib从入门到实战演练!

文章目录 1 什么是Matplotlib1.1 Matplotlib的安装1.2 Matplotlib的基本使用 2 绘制直线3 绘制折线设置标签文字和线条粗细设置中文标题风格的设置 4 绘制曲线绘制曲线yx^2绘制正弦曲线和余弦曲线画布分区 5 绘制散点图绘制不同种类不同颜色的线 6 绘制条形图&#xff08;柱状&…

1. Linux系统(CentOS7.9)安装

toc 一、Linux概述介绍 1、Linux系统介绍 Linux, 一类操作系统的统称 部署在服务器上&#xff0c;部署项目、应用 服务器: 硬件设备, 柜式服务器&#xff0c;(华为、浪潮、联想) 提供服务的机器 2、Linux的优势 开源, open source , 开放源代码稳定性最大化发挥硬件资源 …

【电子通识】案例:连接器接线顺序评估为什么新人总是评估不到位?

在一个IC卡切换的工装板(一切多)中,设计需求是一张PCB(充当活动卡片)插入读卡器,将卡片中的所有信号引出通过连接器连接到后级设备。 比如下图所示是一种IC卡压力测试设备,使用钢片卡片将压力信号通过连接器引入测试设备。 最后根据ISO/IEC 7816-2标准中我们看到…

Mortise AI编程智能体产品 | OPENAIGC开发者大赛企业组AI创作力奖

在第二届拯救者杯OPENAIGC开发者大赛中&#xff0c;涌现出一批技术突出、创意卓越的作品。为了让这些优秀项目被更多人看到&#xff0c;我们特意开设了优秀作品报道专栏&#xff0c;旨在展示其独特之处和开发者的精彩故事。 无论您是技术专家还是爱好者&#xff0c;希望能带给…

c++ 杂项

简答题 1、什么是虚函数&#xff1f;什么是纯虚函数&#xff1f; 虚函数是在类中定义函数时&#xff0c;在函数前加 virtual 关键字。父子类中只有一个该函数。 如果子类中没有重写该虚函数。那么父子类空间中使用的都是父类定义的该函数。 如果子类中重写了该虚函数&#xff…

Leetcode面试经典150题-322.零钱兑换

给你一个整数数组 coins &#xff0c;表示不同面额的硬币&#xff1b;以及一个整数 amount &#xff0c;表示总金额。 计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额&#xff0c;返回 -1 。 你可以认为每种硬币的数量是无限的。 示…

9.26作业

C 面试题 1,什么是虚函数?什么是纯虚函数? 虚函数&#xff1a;父子类中&#xff0c;在父类中的函数需要在子类中进行重写&#xff0c;重写后父子类空间中使用的都是重写后的函数&#xff0c;该函数就是虚函数&#xff0c;虚函数的声明需要在函数前加virtual。 纯虚函数&…

从自身经历浅谈对于C++/Java的认识

1.声明 因为一些其他的原因&#xff0c;我决定从C转到java方向学习&#xff0c;后期可能就要换方向了&#xff0c;以后主要学习这个java相关的这个技术了&#xff0c;起码暂时不会学习这个C里面的内容了&#xff1b; 2.我的感慨 当时选方向的时候&#xff0c;我自己就是选的…

详解 Spring Boot 的 RedisAutoConfiguration 配置

引言 带大家分析 Spring Boot 内置的有关 Redis 的自动配置类【RedisAutoConfiguration】。 1. Spring Data Redis Spring Data Redis 是 Spring Data 家族的一部分&#xff0c;它提供了从 Spring 应用程序中轻松配置和访问 Redis 的功能。 我们来看看官方介绍的特性&#xff…

超60%项目聚焦智能体,百度“文心杯”创业大赛卷起来了

“通过AI Native工具AI Native工作流AI Native创作者协同&#xff0c;我们将传统A级漫画的创作成本降低了62%。”水母智能创始人兼CEO苗奘表示&#xff0c;“4月份决定报名参加‘文心杯’创业大赛&#xff0c;除了百度提供的奖金和资源外&#xff0c;更吸引我的是Robin的理念&a…

Synchronized对字符串上锁?

HTTP去请求就会像上面那种自动加个new String&#xff08;&#xff09;&#xff0c;就会导致锁的线程不是同一个对象&#xff0c;可以通过获取对应常量达到效果 但还有个问题&#xff0c;字符串常量是存在JVM的常量池中。常量池是全局的。所以在其他地方有引用到相关常量时&…

OCI 简介:Kubernetes 环境下从代码到容器的全流程

OCI 简介 在容器化技术的演进中&#xff0c;OCI&#xff08;Open Container Initiative&#xff09;提供了一套标准化的规范&#xff0c;帮助统一容器的构建、分发和运行。OCI 规范包含三个部分&#xff1a; OCI Image-spec&#xff1a;定义了容器镜像的结构&#xff0c;确保…

WAF,全称Web Application Firewall,好用WAF推荐

WAF&#xff0c;全称Web Application Firewall&#xff0c;即Web应用防火墙&#xff0c;是一种网络安全设备&#xff0c;旨在保护Web应用程序免受各种Web攻击&#xff0c;如SQL注入、跨站脚本&#xff08;XSS&#xff09;、跨站请求伪造&#xff08;CSRF&#xff09;等。 WAF通…

STM32堆栈溢出Bug

可以看到x和buf交换位置后&#xff0c;x处于0x200006B0地址上是不会被函数B影响到的&#xff0c;实际上B函数对buf赋值的过程是出现了越界行为的&#xff0c;所以导致了x在buf地址之后的话会被意外修改掉值。

海外媒体投稿:如何运用3种国内外媒体套餐发稿突出重围?

在当今瞬息万变的经营环境中&#xff0c;突出重围营销推广是每家企业都需要思考的问题。为了能突出重围并提升影响力&#xff0c;国内外媒体套餐内容成为了一个非常受欢迎的挑选。下面我们就为大家讲解如何运用三种不同种类的国内外媒体套餐内容来推广突出重围。 2.微博营销新浪…

Nacos笔记

nacos注册中心&#xff1a; nacos注册中心得单击非持久化搭建&#xff1a; 单机&#xff1a;指的是 Nacos 运行在单个实例上&#xff0c;通常用于开发和测试环境。非持久化&#xff1a;表示注册的信息&#xff08;如服务实例、元数据等&#xff09;不会被保存在数据库中。Nac…

Python 从入门到实战29(目录的操作)

我们的目标是&#xff1a;通过这一套资料学习下来&#xff0c;通过熟练掌握python基础&#xff0c;然后结合经典实例、实践相结合&#xff0c;使我们完全掌握python&#xff0c;并做到独立完成项目开发的能力。 上篇文章我们讨论了文件的打开、创建、关闭、读取的相关知识。今天…

智慧政务助力实现服务民生新突破

在数字化转型的浪潮中&#xff0c;中国移动紧密结合人工智能&#xff08;AI&#xff09;技术&#xff0c;推动政务服务的智能化升级。近日&#xff0c;中国移动正式发布政务大模型3.0版本&#xff0c;以科技创新提升政务效率&#xff0c;实现服务民生的新突破。 为什么…

从0到1训练私有大模型技能与应用实现

1.背景 近期&#xff0c;GPT大模型的发布给自然语言处理&#xff08;NLP&#xff09;领域带来了令人震撼的体验。随着这一事件的发生&#xff0c;一系列开源大模型也迅速崛起。依据一些评估机构的评估&#xff0c;这些开源模型大模型的表现也相当不错。一些大模型的评测情况可…