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

news2024/11/16 8:33:10

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

思考

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

这个时间可能不同的编程语言是不同的 比如沙箱执行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
     */
    @Override
    public 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 {

    // 题目服务
    @Resource
    private QuestionService questionService;

    // 题目提交服务
    @Resource
    private QuestionSubmitService questionSubmitService;


    @Value("${codesandbox.type:example}")
    private String value;

    @Override
    public 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.coloradmin.cn/o/2169066.html

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

相关文章

【Mysql】数据库系统和Mysql

1、数据库系统 数据库&#xff08;Database&#xff09;是一个以某种组织方式存储在磁盘上的数据当代集合。 2、数据库应用 数据库应用系统是指基于数据库的应用软件。 3、数据库管理系统&#xff08;数据库软件&#xff09; &#xff08;1&#xff09;关系型数据库&#…

【JAVA高级】 redis分布式双重加锁(业务校验:防止接口并发调用时数据重复)

文章目录 此问题的考虑思路使用Redis的key-value锁的基本思路结合Redis数据结构实现避免重复注意事项实现代码只避免 name和age的重复避免 name和age的和age和sex重复&#xff1a;使用双重的分布式锁实现&#xff1a; 背景&#xff1a;在日常开发过程中&#xff0c;遇到了一个需…

FGPA实验——触摸按键

本文系列都基于正点原子新起点开发板 FPGA系列 1&#xff0c;verlog基本语法&#xff08;随时更新&#xff09; 2&#xff0c;流水灯&#xff08;待定&#xff09; 3&#xff0c;FGPA实验——触摸按键 一、触摸操作原理实现 分类&#xff1a;电阻式&#xff08;不耐用&…

SVN文件不显示修改状态图标

今天安装试用SVN时发现文件不显示修改状态 以下为解决方法&#xff1a; 1&#xff0c;在有.svn的文件夹中右键--tortoiseSvn--setting 2&#xff0c;选中icon Overlays&#xff0c;右侧的status cache 选shell 3&#xff0c;点击icon set 如下图所示 4&#xff0c;修改icon…

MySQL扩展

一、慢查询&#xff08;慢日志&#xff09; 默认关闭的 定位慢SQL 简单&#xff1a;show profile&#xff0c;启用时会对服务器的性能产生额外的负担 -- 启用性能监控 mysql> set profiling1;-- 执行SQL mysql> SELECT * from member-- 性能分析 mysql> show p…

AOT源码解析4.4 -decoder生成预测mask并计算loss

3、生成ref_imgs的预测mask和loss 这一步在训练阶段调用 3.1 数据处理 图1&#xff0c;如图1所示&#xff0c;将enc_embs的最后一个比例的特征图和有ref_imgs相关的特征图得到的LSTT特征图相拼接作为输入 curr_enc_embs self.curr_enc_embscurr_lstt_embs self.curr_lstt_o…

卷轴模式商城APP开发搭建全流程解析

卷轴模式商城APP的开发搭建是一个综合性强、涉及多个关键步骤和技术环节的过程。本文将详细介绍从需求分析到最终发布的各个阶段&#xff0c;旨在为开发者renxb001提供一个清晰的开发指导方案。 一、需求分析 目标用户群体&#xff1a;首先&#xff0c;明确APP的目标用户&…

openKylin--安装 .net6.0

编辑profile文件 cd .. //切换到根目录 cd /etc //切换到etc目录 vim profile //b编辑profile文件 1. 按→键移动到文件末尾 2. 按Insert键进入编辑模式 3. 按Enter另起一行开始编辑 export DOTNET_ROOT/home/dotnetexport PATH$PATH:/home/dotnet 可以通过右键--粘贴 的…

基于skopt的贝叶斯优化基础实例学习实践

贝叶斯方法是非常基础且重要的方法&#xff0c;在前文中断断续续也有所介绍&#xff0c;感兴趣的话可以自行移步阅读即可&#xff1a; 《数学之美番外篇&#xff1a;平凡而又神奇的贝叶斯方法》 《贝叶斯深度学习——基于PyMC3的变分推理》 《模型优化调参利器贝叶斯优化bay…

Brave编译指南2024 MacOS篇-引言与准备工作(一)

引言 随着互联网隐私和安全问题日益突出,用户对安全浏览器的需求不断增加。Brave浏览器作为一款注重隐私保护和性能优化的开源浏览器,吸引了越来越多开发者的关注。本系列文章将详细介绍如何在MacOS环境下编译Brave浏览器,为有兴趣深入了解和定制Brave的开发者提供指导。 1. …

【智能控制】16章 基于Hopfield网络的路径优化,TSP问题

目录 15.6 基于Hopfield网络的路径优化 15.6.1 TSP问题 15.6.2 求解TSP问题的Hopfield神经网络设计 15.6 基于Hopfield网络的路径优化 15.6.1 TSP问题 旅行商问题&#xff08;Traveling Salesman Problem&#xff0c;简称TSP&#xff09;可描述为&#xff1a;已知N个城市之…

CloudMusic:免费听歌

本文所涉及所有资源均在 传知代码平台可获取。 目录 概述 演示效果 视频演示 图片展示 核心逻辑 获取歌曲图片 提取搜索结果 使用方式 部署方式 Docker部署1 构建镜像 Web站点部署2 附件下载 概述 CloudMusic是一款全网歌曲免费听的web项目&#xff0c;无需任何数据库&#x…

如何隐藏Windows10「安全删除硬件」里的USB无线网卡

本方法参照了原文《如何隐藏Windows10「安全删除硬件」里的USB无线网卡》里面的方法&#xff0c;但是文章中的描述我的实际情况不太一样&#xff0c;于是我针对自己的实际情况进行了调整&#xff0c;经过测试可以成功隐藏Windows10「安全删除硬件」里的USB无线网卡。 先说一下…

QT学习笔记之文件操作

你千万不要跟任何人谈起任何事。你只要一谈起&#xff0c;就会想念起每一个人来。 在ui界面添加一个LineEdit(lEt)、QPushButton(btn)、QWidget widget.cpp #include "widget.h" #include "ui_widget.h" #include <QFile> #include <QFileDialo…

node.js从入门到快速开发一个简易的web服务器

浏览器中JavaScript学习路径: JavaScript基础语法浏览器内置API(DOMBOM)第三方库(jQuery,art-template等) Node.js的学习路径 JavaScript基础语法Node.js内置API模块(fs、path、http等)第三方API模块(express、mysql等) Node.js安装 通过Node.js 来运行Javascript 代码&am…

坝上草原与闪电湖多伦湖自驾行程记录与攻略

本文介绍河北坝上草原、内蒙古多伦湖2天2夜自驾自由行&#xff08;坝上草原1日、多伦湖1日&#xff09;的每日详细行程、游览心得、避坑经历等。 2024年09月中秋节期间&#xff0c;我们一行4人从北京出发&#xff0c;自驾前往河北省与内蒙古自治区等2地&#xff0c;进行了一共为…

几个可以给pdf加密的方法,pdf加密详细教程。

几个可以给pdf加密的方法&#xff0c;pdf加密详细教程。在信息快速传播的今天&#xff0c;PDF文件已经成为重要的文档格式&#xff0c;被广泛应用于工作、学习和个人事务中。然而&#xff0c;随着数字内容的增加&#xff0c;数据安全和隐私保护的问题愈发凸显。无论是商业机密、…

高级算法设计与分析 学习笔记9 跳表

单链表的样子我们很熟悉了&#xff1a; 怎么加快查找&#xff1f;&#xff1a; 查找的具体方法&#xff1a; 超过了就回头下去。 这条“快速路”最好是几个节点呢&#xff1f;&#xff1a; 假如我们弄好多层跳表呢&#xff1f;&#xff1a; 给弄成2叉树了&#xff01; 如何插入…

设计模式、系统设计 record part01

技术路线&#xff1a; 工程师》设计师》分析师》架构师 管理路线&#xff1a; 项目经理》技术经理 工程师&#xff1a; 编程技术、测试技术 设计师&#xff1a; 工程师设计技术 分析师&#xff1a; 设计师分析技术 架构师&#xff1a; 分析师架构技术 项目经理&#xff1a; 时间…

发掘3D文件格式的无限潜力:打造沉浸式虚拟世界

在当今数字化时代&#xff0c;3D技术的应用范围日益广泛&#xff0c;涵盖电影后期制作、产品原型设计、虚拟现实&#xff08;VR&#xff09;、增强现实&#xff08;AR&#xff09;、游戏等众多领域。而3D文件格式作为3D技术的核心组成部分&#xff0c;对于实现3D数据和模型的存…