首页 > 其他分享 >OJ在线评测系统 后端 判题机模块预开发 架构分析 使用工厂模式搭建

OJ在线评测系统 后端 判题机模块预开发 架构分析 使用工厂模式搭建

时间:2024-09-26 21:23:38浏览次数:10  
标签:OJ 判题 codesandbox dduo 模块 dduoj 沙箱 import com

判题机模块预开发(架构师)(工厂模式)

判题机模块

是为了把代码交个代码沙箱去处理 得到结果返回

代码沙箱

梳理判题模块和代码沙箱的关系

判题模块:调用代码沙箱 把代码和输入交给代码沙箱去执行

代码沙箱:只负责接受代码和输入 返回编译的结果 不负责判题

这两个模块完全解耦

我们采用API交互

为什么代码沙箱要接受和输出一组运行用例

前提:我们的每道题目有多组测试用例

如果每个用例单独调用一个代码用例 会调用多次接口 需要多次网络运输 程序要多次编译 记录程序的执行状态 重复的代码不重复编译

这是一种常见的性能优化的方法

创建一个新的包

用来放代码沙箱模块

先写一个接口

package com.dduo.dduoj.judge.codesandbox;

public interface CodeSandbox {

    ExecuteCodeRequest executeCode(ExecuteCodeRequest executeCodeRequest);
}

提高通用性

之后我们的项目代码只调用接口

不调用具体的实现类

就不用去修改名称了 便于拓展

写一下实体类

ExecuteCodeRequest请求

package com.dduo.dduoj.judge.codesandbox.model;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ExecuteCodeRequest {
    private List<String> inputList;
    private String code;
    private String language;
}


ExecuteCodeResponse响应

package com.dduo.dduoj.judge.codesandbox.model;

import com.dduo.dduoj.model.dto.question.JudgeConfig;
import com.dduo.dduoj.model.dto.questionsubmit.JudgeInfo;

import java.util.List;

public class ExecuteCodeResponse {
    private List<String> outputList;

    //执行信息
    private String message;

    //执行状态
    private Integer status;

    private JudgeInfo judgeInfo;
}

完善

定义不同的代码沙箱实现类

示例代码沙箱

远程代码沙箱

第三方代码沙箱

架构工作

lombok Builder注解

测试一下

package com.dduo.dduoj.judge.codesandbox;

import com.dduo.dduoj.judge.codesandbox.impl.ExampleCodeSandbox;
import com.dduo.dduoj.judge.codesandbox.impl.RemoteCodeSandbox;
import com.dduo.dduoj.judge.codesandbox.model.ExecuteCodeRequest;
import com.dduo.dduoj.judge.codesandbox.model.ExecuteCodeResponse;

import com.dduo.dduoj.model.enums.QuestionSubmitLanguageEnum;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.Arrays;
import java.util.List;


@SpringBootTest
class CodeSandboxTest {
    @Test
    void executeCode() {
        CodeSandbox codeSandbox = new RemoteCodeSandbox();
        String code = "int main() { }";
        String language = QuestionSubmitLanguageEnum.JAVA.getValue();
        List<String> inputList = Arrays.asList("1 2", "3 4");
        ExecuteCodeRequest executeCodeRequest = ExecuteCodeRequest.builder()
                .code(code)
                .language(language)
                .inputList(inputList)
                .build();
        ExecuteCodeResponse executeCodeResponse = codeSandbox.executeCode(executeCodeRequest);
        Assertions.assertNotNull(executeCodeResponse);
    }
}

工厂模式

但是现在问题是我们把new代码沙箱写死了 如果后面项目要改用其他沙箱

可能要改很多地方的代码

我们要使用工厂模式

根据用具传入的字符串参数 生成对应的代码沙箱实现类

package com.dduo.dduoj.judge.codesandbox;


import com.dduo.dduoj.judge.codesandbox.impl.ExampleCodeSandbox;
import com.dduo.dduoj.judge.codesandbox.impl.RemoteCodeSandbox;
import com.dduo.dduoj.judge.codesandbox.impl.ThirdPartyCodeSandbox;

//代码沙箱工厂 根据字符串参数 创建指定的代码沙箱示例
public class CodeSandboxFactory {

    /*
     * 创建代码沙箱示例
     * @param type 沙箱类型
     * @return
     * */
    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();
        }
    }
}

如果确定代码沙箱示例不会出现线程安全问题

可复用

那么可以使用单例工厂模式

但是这种方式是不可取的 我们应该把这些东西放到配置里面

配置化 去改配置文件 而不是修改字符串

这就叫参数配置化 开发者只需要去修改配置文件 而不是去看项目代码 就能自定义使用项目的更多功能

先在application.yml里面去设置

再在程序里面去读取

示例

package com.dduo.dduoj.judge.codesandbox;

import com.dduo.dduoj.judge.codesandbox.impl.ExampleCodeSandbox;
import com.dduo.dduoj.judge.codesandbox.impl.RemoteCodeSandbox;
import com.dduo.dduoj.judge.codesandbox.model.ExecuteCodeRequest;
import com.dduo.dduoj.judge.codesandbox.model.ExecuteCodeResponse;

import com.dduo.dduoj.model.enums.QuestionSubmitLanguageEnum;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.Arrays;
import java.util.List;


@SpringBootTest
class CodeSandboxTest {

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

    @Test
    void executeCode() {
        CodeSandbox codeSandbox = CodeSandboxFactory.NewInstance(value);
        String code = "int main() { }";
        String language = QuestionSubmitLanguageEnum.JAVA.getValue();
        List<String> inputList = Arrays.asList("1 2", "3 4");
        ExecuteCodeRequest executeCodeRequest = ExecuteCodeRequest.builder()
                .code(code)
                .language(language)
                .inputList(inputList)
                .build();
        ExecuteCodeResponse executeCodeResponse = codeSandbox.executeCode(executeCodeRequest);
        Assertions.assertNotNull(executeCodeResponse);
    }
}

我们要增强代码沙箱的能力

在调用代码沙箱前 输出请求参数 在代码沙箱调用后 输出响应结果日志

package com.dduo.dduoj.judge.codesandbox.impl;

import com.dduo.dduoj.judge.codesandbox.CodeSandbox;
import com.dduo.dduoj.judge.codesandbox.model.ExecuteCodeRequest;
import com.dduo.dduoj.judge.codesandbox.model.ExecuteCodeResponse;
import lombok.extern.slf4j.Slf4j;

//示例代码沙箱 (仅供测试 跑通业务流程)
@Slf4j
public class ExampleCodeSandbox implements CodeSandbox {
    @Override
    public ExecuteCodeResponse executeCode(ExecuteCodeRequest executeCodeRequest) {
        log.info("请求信息"+executeCodeRequest.toString());
        System.out.println("示例代码沙箱");
        return null;
    }
}

思考

我们每一个代码沙箱类都写一个 log.info ?

难道每次调用代码沙箱前后都要执行log ?

我们使用代理模式 提供一个Proxy 来增强代码沙箱的能力

静态代理模式

中介

调用者调用代理类 代理类去调用代码沙箱

代理类还可以做一些额外的功能

不仅不用改变原本的代码沙箱实现类 而且对调用者来说 基本也没有改变

也不需要在每一个调用代码沙箱的地方去统计代码

package com.dduo.dduoj.judge.codesandbox;


import com.dduo.dduoj.judge.codesandbox.model.ExecuteCodeRequest;
import com.dduo.dduoj.judge.codesandbox.model.ExecuteCodeResponse;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@AllArgsConstructor
public class CodeSandboxProxy implements CodeSandbox{
    private CodeSandbox codeSandbox;
    
    @Override
    public ExecuteCodeResponse executeCode(ExecuteCodeRequest executeCodeRequest) {
        log.info("代码沙箱的请求信息"+executeCodeRequest.toString());
        ExecuteCodeResponse executeCodeResponse = codeSandbox.executeCode(executeCodeRequest);
        log.info("代码沙箱的响应信息"+executeCodeResponse.toString());
        return executeCodeResponse;
    }
}

接下来我们就可以去修改调用方式

@Test
void executeCodeByProxy() {
    CodeSandbox codeSandbox = CodeSandboxFactory.NewInstance(value);
    codeSandbox =new CodeSandboxProxy(codeSandbox);
    String code = "int main() { }";
    String language = QuestionSubmitLanguageEnum.JAVA.getValue();
    List<String> inputList = Arrays.asList("1 2", "3 4");
    ExecuteCodeRequest executeCodeRequest = ExecuteCodeRequest.builder()
            .code(code)
            .language(language)
            .inputList(inputList)
            .build();
    ExecuteCodeResponse executeCodeResponse = codeSandbox.executeCode(executeCodeRequest);
    Assertions.assertNotNull(executeCodeResponse);
}

标签:OJ,判题,codesandbox,dduo,模块,dduoj,沙箱,import,com
From: https://blog.csdn.net/qq_30500575/article/details/142578004

相关文章

  • 文件系统:Nodejs `fs` 模块
    node.js中的fs(文件系统)模块是一个用于处理文件系统的强大工具,允许您与服务器上的文件和目录进行交互。它内置于node.js中,因此您无需安装任何额外的东西即可使用它。让我们来探讨一下fs的工作原理及其关键功能。1.什么是fs模块?fs模块提供了一个api,用于以紧密围绕标准......
  • LOJ 6241. 性能优化 I 题解
    题给代码意为\[\begin{align*}&\sum\limits_{i=1}^n\sum\limits_{j=1}^{\lfloor\fracni\rfloor}\sum\limits_{k=1}^j[\gcd(j,k)=1]\\=&\sum\limits_{i=1}^n\sum\limits_{j=1}^{\lfloor\fracni\rfloor}\varphi(j)\\......
  • 如何在CMakeList项目中集成GNU Autotools 构建模块
    背景:我有三个工具A,B,C,其中A,B是原先MakeFile编译的工具,C是原先GNUAutotools自动编译的工具。现在希望使用CMakeList统一构建,我的目录如下:||–A|-----/src|-----CMakeList.txt|–B|-----/src|-----CMakeList.txt|–C|-----autoTool|–CMakeList.txt想要起到的效......
  • nav2的behavior模块,controller模块,lifecycle_manager模块
    Navigation2-main项目中nav2_behaviors、controller和lifecycle_manager文件夹的源代码详解在前述章节中,我们已经详细介绍了constrained_smoother、collision_monitor、mppi_controller、dwb_controller、rotation_shim和core模块。本节将继续介绍navigation2-ma......
  • Android平台RTMP推送模块的设计意义
    为什么要做RTMP推送RTMP是一种广泛使用的流媒体传输协议,它允许视频和音频数据在互联网上实时、高效地传输。实现RTMP推送功能,主要是为了满足以下需求:实时性要求:RTMP协议具有低延迟的特点,适合用于需要实时交互的场景,如直播、视频会议等。通过RTMP推送,观众可以几乎实时地观看到主播的......
  • 致远OA公文管理模块相关操作
    目录1、系统模块管理-开启公文2、角色权限分配-分配管理员3、公文插件参数修改4、公文文单制作5、配置发文单6、配置收文单7、配置签报单8、配置签收单9、公文管理-文单设置功能按钮介绍10、模板管理11、节点权限12、节点权限——续办13、印章管理14、文号管理—......
  • 06 常用内置模块总结
    -其他需背会len获取长度openrange随机生成数id是比较内存地址is/==是进行比较type获取数据类型输入输出printinput强制转换dict()list()tuple()int()str()bool()set()数学相关abs,绝对值v=abs(-1)print(v)float,转换成浮点型(小数)v=55v1=......
  • 基于autojs的多平台多功能养号+获客+高级UI界面的软件
    1.项目介绍源码已经完成了完整RPA产品的开发,可进行二次开发或者个人使用其中包括高级UI界面和UI逻辑xhs,快手,微信视频号,抖音的完整养号功能10大针对平台定制化的截流获客功能卡密系统验证对接与日期显示无障碍自启定时启动可穿透悬浮窗功能历史运行日志github软件项目下载......
  • Exadata中的Infiniband交换机,更换完SSD模块后,交换机反复重启
    1.故障描述客户的ExadataX5-2, 某天收到EM13c告警,提示Exadata中的Infiniband交换机出现故障。 2.故障处理2.1 执行showunhealthy命令后,显示告警信息。#showunhealthyWARNINGFlashdiskhasbadblocks.FALURE-1sensorsNOTOK从命令输出可以看了,该Infiniband......
  • 题解 QOJ5034【>.<】/ BC2401D【可爱路径】
    必可赛前公益众筹赛第一试Dhttps://qoj.ac/problem/5034,2022-2023集训队互测Round6(Nov12,2022)题目描述这原本是一道简单的最短路问题,但是由于种种地域文化,宗教信仰以及政治因素,原来一些或许可以行走的路径不能通行了。我们定义禁止路径为连续的经过一些特定的点的......