《leetcode-runner》如何手搓一个debug调试器——架构

本文主要聚焦leetcode-runner对于debug功能的整体设计,并讲述设计原因以及存在的难点

设计引入

让我们来思考一下,一个最简单的调试器需要哪些内容

首先,它能够接受用户的输入

其次,它能够读懂用户想让调试器干嘛,并做出相应的行为

最后,执行完操作后需要显示结果

基于此,我们可以给出一个最简单的设计

在这里插入图片描述

  • Reader:负责读取用户的输入,识别输入并转化为系统能够识别的信息
  • Executor:根据Reader解读分析得到的数据,执行具体的调试行为,产生执行结果
  • Output:根据执行结果,进行可视化显示

这个最简单的设计看似还凑活,实际上确实只能凑活,且不论用户该输入啥,单说Reader,哥们你该解析出个啥呢?解析啥才能让executor识别,并执行对应逻辑

因此,我们还需要引入一套非常重要的系统——指令系统

在这里插入图片描述

当引入指令系统后,整个debug模块都被盘活了。debug的每个模块都能识别指令系统,只要遇到某条指令,就会执行对应的操作

而reader就负责将用户输入的命令行字符串解析转换为指令,然后系统就会依照指令执行相应逻辑

有了指令系统,debug项目完整了吗?No No No

让我们将视线聚焦到executor上,既然是调试代码,那么被调试的代码有被运行吗?被谁运行了呢?在我们目前的架构中是不存在相应的模块。也就是说,我们缺少了调试服务,因此进一步完善,得到如下架构图

在这里插入图片描述

调试服务运行被调试的代码,然后获取得到被调试代码相应信息

executor服务负责与调试服务沟通,通知调试服务执行相应逻辑;调试服务将数据信息存储,交由executor取用,获取目标代码相关信息

接下来,再让我们结合具体场景思考

现在,用户在使用leetcode-runner,编写了一个Solution的class,然后他想要使用debug调试功能,请问,我们能直接调试Solution代码吗?

调试个屁!要是能这么轻松,我还写个屁的调试器!

首先,项目需要为用户自动编写一个Main类,也就是入口函数。此外,还需要将测试案例转换成对应代码,比如’[1,2,3]',转换成Java代码就是int[] a = new int[] {1,2,3};,然后实例化Solution class,调用相应的方法,同时将测试案例转换得到的变量传入方法内,这才算完成前置工作

像这类前置工作的准备,leetcode-runner统一将其封装在DebugEnv对象内,DebugEnv提供的prepare()方法就是为了完成前置工作

进一步完善,可以得到如下架构图

在这里插入图片描述

现在,整个系统像样了不少,但如果想要暴露给外部系统使用,还是麻烦了点,如果想要别人使用方便,我们该怎么办呢?答案是——封装!

现在,我们引入Debugger类,负责启动整个debug框架,得到如下的架构图

在这里插入图片描述

接下来进一步完善细节,将leetcode-runner负责各个模块的核心类名填入系统,得到如下架构图

请添加图片描述

为什么这么设计

以笔者粗俗的理解,设计的目的是为了更好的编写代码。一个好的设计可以避免出现非常对的bug,在一定程度上提高编程的速度

让我们回看上方做出的设计,我们不难发现,所有的功能都被封装在独立的模块之内。模块与模块之间并没有过强的耦合,不会出现牵一发而动全身的情况

另外,如此设计还有一个好处——不依赖具体的语言。啥意思呢?打个比方,我现在要做Java的debug调试器,我需要实现的子类有DebugEnvDebuggerExecuteContext调试服务InstExecutor。其他的模块内容完全可以复用,比如InstReaderOutput,指令系统…因为这些模块不依赖于具体的语言

当我需要实现一个python的debug调试器,同样可以复用这些模块。此外,DebugEnv,Debugger也存在可以复用逻辑。

DebugEnv,不同语言总有相同的准备活动,比如可执行程序(jdk,python解释器…)测试案例准备(程序输入)代码创建(Main函数,也就是程序入口)…因为有如此多的共用逻辑,完全可以提取到父类当中,在leetcode-runner中,就提供了AbstractDebugEnv封装公用逻辑

Debugger,作为debug框架入口,如读取执行可视化这一套流程,就可以复用

存在难点

1. 环境准备

在环境准备的过程中,我们最需要关注的是Main函数的创建,这里给一个leetcode-runner创建的Main函数,以leetcode-1367题目为例。leetcode-runner根据该题提供的Solution代码片段创建相应的Main函数

Solution代码片段

/*** Definition for a binary tree node.* public class TreeNode {*     int val;*     TreeNode left;*     TreeNode right;*     TreeNode() {}*     TreeNode(int val) { this.val = val; }*     TreeNode(int val, TreeNode left, TreeNode right) {*         this.val = val;*         this.left = left;*         this.right = right;*     }* }*/
class Solution {public boolean isSubPath(ListNode head, TreeNode root) {}
}

插件项目自动生成的Main文件

import java.util.*;
/**/
public class Main {public static void main(String[] args) {Solution solution = new Solution();String bxdnslspts = "[4,2,8]";bxdnslspts = bxdnslspts.trim();ListNode a0 = null;if (!"[]".equals(bxdnslspts)) {// 把collect变为数组Integer[] split =Arrays.stream(bxdnslspts.replace("[", "").replace("]", "").split(",")).map(Integer::parseInt).toArray(Integer[]::new);int i = 0;a0 = new ListNode(split[i]);ListNode cp = a0;i += 1;// 迭代for (; i < split.length; ++i) {cp.next = new ListNode(split[i]);cp = cp.next;}}String cnuepugbsq = "[1,4,4,null,2,2,null,1,null,6,8,null,null,null,null,1,3]";cnuepugbsq = cnuepugbsq.trim();TreeNode a1 = null;if (!"[]".equals(cnuepugbsq)) {// 把collect变为数组Integer[] split =Arrays.stream(cnuepugbsq.replace("[", "").replace("]", "").split(",")).map(e -> "null".equals(e) ? null : (int) Integer.parseInt(e)).toArray(Integer[]::new);int i = 0;a1 = new TreeNode(split[i]);i++;Queue<TreeNode> q = new LinkedList<>();q.add(a1);while (!q.isEmpty()) {TreeNode node = q.poll();// 添加它的左节点if (i < split.length) {if (split[i] != null) {node.left = new TreeNode(split[i]);q.add(node.left);}i += 1;}// 添加右节点if (i < split.length) {if (split[i] != null) {node.right = new TreeNode(split[i]);q.add(node.right);}i += 1;}}}solution.isSubPath(a0, a1);}
}

这里的难点是,如何根据Solution的核心代码片段,创建对应的Main函数。此外,还需要通过测试案例,转换为相应的代码

2. 调试程序

调试程序,这部分与语言强相关,并且非常底层。如果你是Java的调试器开发,你将明白Java在底层到底封装了多少内容,提供了多大的便利。光是自动拆箱自动装箱,在调试程序编写时都需要手动处理判断,巨tm麻烦

另外,调试程序的逻辑处理,执行顺序,以及最复杂的表达式计算,这些都是调试程序编写的难点

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

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

相关文章

电商项目-基于ElasticSearch实现商品搜索功能(三)

本系列文章主要介绍基于 Spring Data Elasticsearch 实现商品搜索的后端代码&#xff0c;介绍代码逻辑和代码实现。 主要实现功能&#xff1a;根据搜索关键字查询、条件筛选、规格过滤、价格区间搜索、搜索查询分页、搜索查询排序、高亮查询。 主要应用技术:canal&#xff0c;…

C++ using(八股总结)

using作用&#xff1a; 类型别名using声明using指示 类型别名 using 可以用来创建类型别名&#xff0c;替代传统的 typedef。这在定义复杂类型时尤其有用&#xff0c;例如模板类型。 // 使用 typedef 创建类型别名 typedef long long ll;// 使用 using 创建类型别名 using …

linux mysql 备份

直接上代码 一&#xff0c;安装cron 1 安装cron sudo yum install cronie2 启动cron sudo systemctl start crond3 设置开机启动 sudo systemctl enable crond4 查看任务 crontab -l 没有任务则显示&#xff1a;no crontab for root二&#xff0c;备份脚本 #!/bin/bash # 备份…

【0x005B】HCI_Write_Default_Erroneous_Data_Reporting命令详解

目录 一、命令概述 二、命令格式及参数 2.1. HCI_Write_Default_Erroneous_Data_Reporting命令格式 2.2. Erroneous_Data_Reporting 三、生成事件及参数 3.1. HCI_Command_Complete事件 3.2. 状态码(Status) 四、命令执行流程 4.1. 命令发起阶段(主机端) 4.2. 命…

uniapp 小程序 textarea 层级穿透,聚焦光标位置错误怎么办?

前言 在开发微信小程序时&#xff0c;使用 textarea 组件可能会遇到一些棘手的问题。最近我在使用 uniapp 开发微信小程序时&#xff0c;就遇到了两个非常令人头疼的问题&#xff1a; 层级穿透&#xff1a;由于 textarea 是原生组件&#xff0c;任何元素都无法遮盖住它。当其…

FLASK创建下载

html用a标签 <!-- Button to download the image --> <a href"{{ url_for(download_file, filenameimage.png) }}"><button>Download Image</button> </a> 后端&#xff1a;url_for双大括号即是用来插入变量到模板中的语法。也就是绑…

Kotlin 快速上手指南:从安装 IntelliJ IDEA 到编写第一个程序

文章目录 什么是kotlinIntelliJ IDEA安装 IntelliJ IDEA创建 Kotlin 项目运行 Kotlin 程序更改进入后默认打开上一次项目的设置打开 IntelliJ IDEA进入设置:重新启动 IntelliJ IDEA:快速学习Kotlin变量声明类型推断条件表达式定义函数单表达式函数when 表达式when 语句的基本…

Docker 部署 Typecho

1. 官网 https://typecho.org/插件 & 主题 https://github.com/typecho-fans/plugins https://typechx.com/ https://typecho.work/2. 通过 compose 文件安装 github官网&#xff1a; https://github.com/typecho/Dockerfile 新建一个目录&#xff0c;存放 typecho 的相…

2025/1/12 复习JS

我乞求你别再虚度光阴 ▶ 空心 --------------------------------------------------------------------------------------------------------------------------------- 摘自哔哩哔哩听课笔记。 01 上篇&#xff1a;核心语法 1.基于页面效果的操作 <!DOCTYPE html>…

【机器学习】主动学习-增加标签的操作方法-样本池采样(Pool-Based Sampling)

Pool-Based Sampling Pool-based sampling 是一种主动学习&#xff08;Active Learning&#xff09;方法&#xff0c;与流式选择性采样不同&#xff0c;它假设有一个预先定义的未标注样本池&#xff0c;算法从中选择最有价值的样本进行标注&#xff0c;以提升模型的性能。这种…

【初识扫盲】厚尾分布

厚尾分布&#xff08;Fat-tailed distribution&#xff09;是一种概率分布&#xff0c;其尾部比正态分布更“厚”&#xff0c;即尾部的概率密度更大&#xff0c;极端值出现的概率更高。 一、厚尾分布的特征 尾部概率大 在正态分布中&#xff0c;极端值&#xff08;如距离均值很…

机组存储系统

局部性 理论 程序执行&#xff0c;会不均匀访问主存&#xff0c;有些被频繁访问&#xff0c;有些很少被访问 时间局部性 被用到指令&#xff0c;不久可能又被用到 产生原因是大量循环操作 空间局部性 某个数据和指令被使用&#xff0c;附近数据也可能使用 主要原因是顺序存…

Transformer创新模型!Transformer+BO-SVR多变量回归预测,添加气泡图、散点密度图(Matlab)

Transformer创新模型&#xff01;TransformerBO-SVR多变量回归预测&#xff0c;添加气泡图、散点密度图&#xff08;Matlab&#xff09; 目录 Transformer创新模型&#xff01;TransformerBO-SVR多变量回归预测&#xff0c;添加气泡图、散点密度图&#xff08;Matlab&#xff0…

31_搭建Redis分片集群

Redis的主从复制模式和哨兵模式可以解决高可用、高并发读的问题。但是依然有两个问题没有解决:海量数据存储问题、高并发写的问题。由于数据量过大,单个master复制集难以承担,因此需要对多个复制集进行集群,形成水平扩展每个复制集只负责存储整个数据集的一部分,这就是Red…

c++ 中的容器 vector、deque 和 list 的区别

表格汇总&#xff1a; 容器存储结构随机访问性能中间插入/删除性能两端插入/删除性能内存管理特点迭代器类型适用场景vector连续存储的动态数组 O ( 1 ) O(1) O(1) O ( n ) O(n) O(n)&#xff08;需要移动元素&#xff09;末尾&#xff1a; O ( 1 ) O(1) O(1)&#xff0c;头部…

ASP.NET Core - 日志记录系统(二)

ASP.NET Core - 日志记录系统&#xff08;二&#xff09; 2.4 日志提供程序2.4.1 内置日志提供程序2.4.2 源码解析 本篇接着上一篇 ASP.NET Core - 日志记录系统(一) 往下讲&#xff0c;所以目录不是从 1 开始的。 2.4 日志提供程序 2.4.1 内置日志提供程序 ASP.NET Core 包括…

nginx的可视化配置工具nginxWebUI的使用

文章目录 1、nginx简介2、nginxWebUI2.1、技术解读2.2、开源版和专业版之间的区别2.3、功能解读 3、安装与使用3.1、下载镜像3.2、查看镜像3.3、启动容器3.4、使用 4、总结 1、nginx简介 Nginx 是一个高效的 HTTP 服务器和反向代理&#xff0c;它擅长处理静态资源、负载均衡和…

【C++】IO 流

文章目录 &#x1f449;C 语言的输入与输出&#x1f448;&#x1f449;流是什么&#x1f448;&#x1f449;C IO 流&#x1f448;C 标准 IO 流C 和 C 语言的输入格式问题C 的多次输入内置类型和自定义类型的转换日期的多次输入C 文件 IO 流文本文件和二进制文件的读写 &#x1…

JavaScript系列(25)--性能优化技术详解

JavaScript性能优化技术详解 ⚡ 今天&#xff0c;让我们深入探讨JavaScript的性能优化技术。掌握这些技术对于构建高性能的JavaScript应用至关重要。 性能优化基础 &#x1f31f; &#x1f4a1; 小知识&#xff1a;JavaScript性能优化涉及多个方面&#xff0c;包括代码执行效…