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

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

判题机模块

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

代码沙箱

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

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

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

这两个模块完全解耦

我们采用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 {@Testvoid 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;@Testvoid 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 {@Overridepublic 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;@Overridepublic 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);
}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

用户态缓存:环形缓冲区(Ring Buffer)

目录 环形缓冲区&#xff08;Ring Buffer&#xff09;简介 为什么选择环形缓冲区&#xff1f; 代码解析 1. 头文件与类型定义 1.1 头文件保护符 1.2 包含必要的标准库 1.3 类型定义 2. 环形缓冲区结构体 2.1 结构体成员解释 3. 辅助宏与内联函数 3.1 min 宏 3.2 is…

Python办公自动化案例:将Excel数据批量保存到Word表格中

案例:将excel数据批量保存到Word表格中 要将Excel数据批量保存到Word表格中,可以使用Python的openpyxl库来读取Excel文件,以及python-docx库来创建和编辑Word文档。以下是一段示例代码,以及代码解释和一些注意事项。 准备好的Excel数据: 1.安装所需库 首先,确保你已经…

破局汽车智能化浪潮:Tire 1供应商的网络优化与升级策略

在汽车行业经历电动化、智能化的深刻变革中&#xff0c;Tier 1供应商正面临着前所未有的挑战与机遇。Tier 1 供应商&#xff0c;即一级供应商&#xff0c;是汽车产业链中占据关键地位的合作伙伴。这类供应商不仅直接向整车制造商提供核心总成和模块&#xff0c;还深度参与整车的…

通信工程学习:什么是PNF物理网络功能

PNF:物理网络功能 PNF(Physical Network Function)即物理网络功能,是指支持网络功能的物理设备。以下是关于PNF的详细解释: 一、定义与特点 定义: PNF是网络设备厂商(如Cisco、华为、H3C等)通过专用硬件实体提供软件功能的设备。这些设备直接在物理服务器上运…

C++_实现日期类

✨✨ 欢迎大家来到小伞的大讲堂✨✨ &#x1f388;&#x1f388;养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 所属专栏&#xff1a;C学习 小伞的主页&#xff1a;xiaosan_blog 1.日期类的实现接口(date.h) 对于多次调用的函数&#xff0c;我们会实现在头文件…

免杀笔记 ---> 一种有想法的Indirect-Syscall

今天来分享一下&#xff0c;看到的一种Indirect-Syscall&#xff0c;也是两年前的项目了&#xff0c;但是也是能学到思路&#xff0c;从中也是能感受到杀软对抗之间的乐趣&#xff01;&#xff01;说到乐趣&#xff0c;让我想起看到过一位大佬的文章对"游褒禅山记"的…

Spring Boot电商开发:购物商城系统

第2章 关键技术简介 2.1 Java技术 Java是一种非常常用的编程语言&#xff0c;在全球编程语言排行版上总是前三。在方兴未艾的计算机技术发展历程中&#xff0c;Java的身影无处不在&#xff0c;并且拥有旺盛的生命力。Java的跨平台能力十分强大&#xff0c;只需一次编译&#xf…

算法-Init

&#xff08;1&#xff09;有限性&#xff08;Finiteness&#xff09;&#xff1a;算法必 需在有限步骤内结束&#xff1b; &#xff08;2&#xff09;确定性&#xff08;Definiteness&#xff09;&#xff1a;算法的每一个步骤必须清晰无歧义地定义&#xff1b; &#xff08;3…

基于GIKT深度知识追踪模型的习题推荐系统源代码+数据库+使用说明,后端采用flask,前端采用vue

基于GIKT深度知识追踪模型的习题推荐系统 目录结构 Flask-BackEnd flask后端 app 后端主体文件 alg 深度学习模块 data 数据集data_process.py 数据预处理gikt.py GIKT模型pebg.py PEBG模型params.py 一些参数train.py 仅模型训练train_test.py 模型训练和测试-五折交叉验证t…

51单片机-AD(模拟信号转数字信号)-实验(电压采集)

介绍AD AD转换&#xff08;Analog to Digital Conversion&#xff0c;模数转换&#xff09;是将连续的模拟信号转换为离散的数字信号的过程。这个过程在各种电子设备中都非常重要&#xff0c;特别是在涉及传感器、音频信号、视频信号等需要进行数字化处理的领域。 个人理解&a…

《深度学习》ResNet残差网络、BN批处理层 结构、原理详解

目录 一、关于ResNet 1、什么是ResNet 2、传统卷积神经网络存在的问题 1&#xff09;梯度消失和梯度爆炸问题 2&#xff09;训练困难 3&#xff09;特征表示能力受限 4&#xff09;模型复杂度和计算负担 3、如何解决 1&#xff09;解决梯度问题 BN层重要步骤&#xff1a; 2…

mqtt网关数据接入rabbitmq,缓存离线数据,实现消息保留

应用场景&#xff1a;网关将设备数据发布至mqtt服务器后&#xff0c;数采程序因为重启或者升级等原因&#xff0c;未能接到到离线的订阅消息&#xff0c;利用rabbitmq-mqtt可将离线数据缓存&#xff0c;待上线后接收 启用mqtt插件 rabbitmq-plugins enable rabbitmq_mqtt

C++:STL详解(二)string类的模拟实现

✨ Blog’s 主页: 白乐天_ξ( ✿&#xff1e;◡❛) &#x1f308; 个人Motto&#xff1a;他强任他强&#xff0c;清风拂山冈&#xff01; &#x1f4ab; 欢迎来到我的学习笔记&#xff01; &#x1f525;&#x1f525;&#x1f525;&#x1f525;&#x1f525;本文参考文章&…

【HarmonyOS】深入理解@Observed装饰器和@ObjectLink装饰器:嵌套类对象属性变化

【HarmonyOS】深入理解Observed装饰器和ObjectLink装饰器&#xff1a;嵌套类对象属性变化 前言 之前就Observed和ObjectLink写过一篇讲解博客【HarmonyOS】 多层嵌套对象通过ObjectLink和Observed实现渲染更新处理&#xff01; 其中就Observe监听类的使用&#xff0c;Object…

【Linux 报错】vim 保存文件时出现 E45: ‘readonly‘ option is set (add ! to override)

一、错误原因 该错误表明当前你尝试保存的是一个 只读文件&#xff0c;该文件权限设置为只读&#xff0c;具有只读的标识 系统为了防止你意外修改该只读文件&#xff0c;因此会阻止对只读文件的保存&#xff08;他怕你修改了你还保存&#xff0c;破坏了只读属性&#xff09; …

【Linux扩容根分区】LVM分区扩容过程踩坑记录

最近想要给自己使用的Linux操作系统的根分区进行扩容&#xff0c;解决完发现&#xff0c;原来问题如此简单。 特此记录&#xff0c;希望能帮助到有需要的人。 通过df -Th查看系统磁盘分区情况 通过vgdisplay 查看内容 实操过程中&#xff0c;原来红框中&#xff0c;Free PE …

变电站设备检测系统源码分享

变电站设备检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer V…

webview2加载本地页面

加载方式 通过导航到文件 URL 加载本地内容 使用方式&#xff1a; webView->Navigate( L"file:///C:/Users/username/Documents/GitHub/Demos/demo-to-do/index.html"); 但是这种方式存在一些问题&#xff0c;比如&#xff1a; 存在跨域问题&#xff08;我加载…

RocketMQ 常用命令mqadmin与控制面板

使⽤发送和接收消息验证MQ 验证生产者&#xff1a; 配置 nameserver 的环境变量 &#xff0c;在发送/ 接收消息之前&#xff0c;需要告诉客户端 nameserver 的位置。配置环境变量 NAMESRV_ADDR &#xff1a; vim /etc/profileexport NAMESRV_ADDR"192.168.136.66:9876;1…

【网络安全】网络基础第一阶段——第一节:网络协议基础---- OSI与TCP/IP协议

从今天起&#xff0c;我们正式进入第二部分——网络基础。继续学习网络基础、网络协议等相关内容&#x1f31f;&#x1f31f;&#x1f31f; 目录 一、OSI模型 1.1 分层思想 1.2 OSI参考模型 1.3 数据封装与解封装 1.3.1 数据的封装过程 1.3.2 数据的解封装过程 二、TCP/…