《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,一经查实,立即删除!

相关文章

【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;任何元素都无法遮盖住它。当其…

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>…

【初识扫盲】厚尾分布

厚尾分布&#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…

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…

基于springboot的幼儿园管理系统系统

作者&#xff1a;学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等 文末获取“源码数据库万字文档PPT”&#xff0c;支持远程部署调试、运行安装。 项目包含&#xff1a; 完整源码数据库功能演示视频万字文档PPT 项目编码&#xff1…

Pycharm 使用教程

一、基本配置 1. 切换Python解释器 pycharm切换解释器版本 2. pycharm虚拟环境配置 虚拟环境的目的&#xff1a;创建适用于该项目的环境&#xff0c;与系统环境隔离&#xff0c;防止污染系统环境&#xff08;包括需要的库&#xff09;虚拟环境配置存放在项目根目录下的 ven…

Java设计模式——单例模式(特性、各种实现、懒汉式、饿汉式、内部类实现、枚举方式、双重校验+锁)

文章目录 单例模式1️⃣特性&#x1f4aa;单例模式的类型与实现&#xff1a;类型懒汉式实现(线程不安全)懒汉式实现(线程安全&#xff09;双重锁校验懒汉式(线程安全)饿汉式实现(线程安全)使用类的内部类实现⭐枚举方式实现单例&#xff08;推荐&#xff09;&#x1f44d; 单例…

STM32 FreeRTOS中断管理

目录 FreeRTOS的中断管理 1、STM32中断优先级管理 2、FreeRTOS任务优先级管理 3、寄存器和内存映射寄存器 4、BASEPRI寄存器 5、FreeRTOS与STM32中断管理结合使用 vPortRaiseBASEPRI vPortSetBASEPRI 6、FromISR后缀 7、在中断服务函数中调用FreeRTOS的API函数需注意 F…

[Spring] SpringCloud概述与环境工程搭建

&#x1f338;个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 &#x1f3f5;️热门专栏: &#x1f9ca; Java基本语法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm1001.2014.3001.5482 &#x1f355; Collection与…

mobaxterm内置编辑器中文出现乱码如何解决:直接更换编辑器为本地编辑器

诸神缄默不语-个人CSDN博文目录 使用场景是我需要用mobaxterm通过SSH的方式登录服务器&#xff0c;进入服务器之后我就直接打开代码文件&#xff0c;mobaxterm会直接用内置的编辑器&#xff08;MobaTextEditor&#xff09;打开&#xff0c;但这会导致中文编程乱码。 我一开始是…

机器学习与人工智能的关系

机器学习与人工智能的关系 一、人工智能二、机器学习2.1 机器学习与人工智能的关系2.2 机器学习的本质 三、其他玩艺 曾几何时&#xff0c;人工智能还是个科幻名词&#xff0c;仿佛只属于未来世界。如今&#xff0c;它已经渗透到了我们生活的方方面面&#xff0c;成为顶流。我们…