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

news2025/1/15 15:12:24

本文主要聚焦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.coloradmin.cn/o/2277066.html

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

相关文章

食堂采购系统源码:基于PHP的校园食堂供应链管理平台开发全解析

传统的食堂采购管理普遍存在信息不透明、流程繁琐、效率低下等问题&#xff0c;这使得开发一款高效、智能的食堂采购系统变得尤为重要。本篇文章&#xff0c;笔者将详细解析基于PHP开发的校园食堂供应链管理平台&#xff0c;从功能设计、系统架构到技术实现&#xff0c;全方位剖…

WEB 攻防-通用漏-XSS 跨站脚本攻击-反射型/存储型/DOMBEEF-XSS

XSS跨站脚本攻击技术&#xff08;一&#xff09; XSS的定义 XSS攻击&#xff0c;全称为跨站脚本攻击&#xff0c;是指攻击者通过在网页中插入恶意脚本代码&#xff0c;当用户浏览该网页时&#xff0c;恶意脚本会被执行&#xff0c;从而达到攻击目的的一种安全漏洞。这些恶意脚…

重生之我在21世纪学C++—函数与递归

一、函数是什么&#xff1f; 相信我们第一次接触函数是在学习数学的时候&#xff0c;比如&#xff1a;一次函数 y kx b &#xff0c;k 和 b 都是常数&#xff0c;给一个任意的 x &#xff0c;就会得到一个 y 值。 其实在 C 语言中就引入了函数&#xff08;function&#xf…

Mac——Cpolar内网穿透实战

摘要 本文介绍了在Mac系统上实现内网穿透的方法&#xff0c;通过打开远程登录、局域网内测试SSH远程连接&#xff0c;以及利用cpolar工具实现公网SSH远程连接MacOS的步骤。包括安装配置homebrew、安装cpolar服务、获取SSH隧道公网地址及测试公网连接等关键环节。 1. MacOS打开…

Ubuntu中双击自动运行shell脚本

方法1: 修改文件双击反应 参考: https://blog.csdn.net/miffywm/article/details/103382405 chmod x test.sh鼠标选中待执行文件&#xff0c;在窗口左上角edit菜单中选择preference设计双击执行快捷键&#xff0c;如下图&#xff1a; 方法2: 设置一个应用 参考: https://blo…

力扣 全排列

回溯经典例题。 题目 通过回溯生成所有可能的排列。每次递归时&#xff0c;选择一个数字&#xff0c;直到选满所有数字&#xff0c;然后记录当前排列&#xff0c;回到上层时移除最后选的数字并继续选择其他未选的数字。每次递归时&#xff0c;在 path 中添加一个新的数字&…

arcgis提取不规则栅格数据的矢量边界

效果 1、准备数据 栅格数据:dem或者dsm 2、栅格重分类 分成两类即可 3、新建线面图层 在目录下选择预先准备好的文件夹,点击右键,选择“新建”→“Shapefile”,新建一个Shapefile文件。 在弹出的“新建Shapefile”对话框内“名称”命名为“折线”,“要素类型”选…

【DB-GPT】开启数据库交互新篇章的技术探索与实践

一、引言&#xff1a;AI原生数据应用开发的挑战与机遇 在数字化转型的浪潮中&#xff0c;企业对于智能化应用的需求日益增长。然而&#xff0c;传统的数据应用开发方式面临着诸多挑战&#xff0c;如技术栈复杂、开发周期长、成本高昂、难以维护等。这些问题限制了智能化应用的…

LVGL移植高通点阵字库GT30L24A3W

字库芯片: GT30L24A3W MCU:STM32F429 LVGL版本:V8.4 一、实现gt_read_data() 和 r_dat_bat() 请参考下面视频 如何在32位MCU上使用高通点阵字库_哔哩哔哩_bilibili 高通字库使用教程(1)硬件链接与注意事项部分_哔哩哔哩_bilibili 高通字库使用教程(2)SPI底层函数使用_哔哩…

一键掌握多平台短视频矩阵营销/源码部署

短视频矩阵系统的介绍与应用 随着数字化营销策略的不断演进&#xff0c;传统的短视频矩阵操作方法可能已显陈旧。为此&#xff0c;一款全新的短视频矩阵系统应运而生&#xff0c;它通过整合多个社交媒体账户、创建多样化的任务、运用先进的智能视频编辑工具、实现多平台内容的…

MySQL(高级特性篇) 06 章——索引的数据结构

一、为什么使用索引 索引是存储引擎用于快速找到数据记录的一种数据结构&#xff0c;就好比一本教科书的目录部分&#xff0c;通过目录找到对应文章的页码&#xff0c;便可快速定位到需要的文章。MySQL中也是一样的道理&#xff0c;进行数据查找时&#xff0c;首先查看查询条件…

源码安装httpd2.4

1、下载 wget https://archive.apache.org/dist/httpd/httpd-2.4.54.tar.gz 2.解压下载压缩包 tar -zxvf httpd-2.4.54.tar.gz cd httpd-2.4.54 3、安装httpd所需要的依赖 yum groupinstall "Development Tools" -y 4.配置httpd ./configure --prefix/usr/local/htt…

【算法学习】——整数划分问题详解(动态规划)

&#x1f9ee;整数划分问题是一个较为常见的算法题&#xff0c;很多问题从整数划分这里出发&#xff0c;进行包装&#xff0c;形成新的题目&#xff0c;所以完全理解整数划分的解决思路对于之后的进一步学习算法是很有帮助的。 「整数划分」通常使用「动态规划」解决&#xff0…

文件与IO流:一

一些常识 硬盘特点 擅长顺序读&#xff0c;不擅长随机读&#xff0c;尤其是机械硬盘。 随机读例如某个目录中的所有小文件的复制&#xff0c;顺序读是某个大文件的整体复制。 windows的文件系统是按照“树形结构”来组织文件。 路径的风格 1.绝对路径&#xff1a;从根节点…

计算机网络 (42)远程终端协议TELNET

前言 Telnet&#xff08;Telecommunication Network Protocol&#xff09;是一种网络协议&#xff0c;属于TCP/IP协议族&#xff0c;主要用于提供远程登录服务。 一、概述 Telnet协议是一种远程终端协议&#xff0c;它允许用户通过终端仿真器连接到远程主机&#xff0c;并在远程…

WPF系列十二:图形控件CombinedGeometry

简介 CombinedGeometry 是 WPF (Windows Presentation Foundation) 中的一个几何对象&#xff0c;用于将两个几何图形组合成一个新的几何图形。它允许你通过不同的组合模式&#xff08;如相交、并集、差集或异或&#xff09;来创建复杂的形状。常与 Path 控件一起使用来绘制组…

《计算机网络》课后探研题书面报告_网际校验和算法

网际校验和算法 摘 要 本文旨在研究和实现网际校验和&#xff08;Internet Checksum&#xff09;算法。通过阅读《RFC 1071》文档理解该算法的工作原理&#xff0c;并使用编程语言实现网际校验和的计算过程。本项目将对不同类型的网络报文&#xff08;包括ICMP、TCP、UDP等&a…

业务幂等性技术架构体系之接口幂等深入剖析

在实际应用中&#xff0c;由于网络不稳定、系统延迟等原因&#xff0c;客户端可能会重复发送相同的请求。如果这些重复请求都被服务器处理并执行&#xff0c;就可能导致意想不到的问题&#xff0c;比如重复扣款、多次下单或者数据不一致等。 这就是为什么我们需要接口幂等性。…

sql模糊关联匹配

需求目标&#xff1a; 建立临时表 drop table grafana_bi.zbj_gift_2024;USE grafana_bi; CREATE TABLE zbj_gift_2024 (id INT AUTO_INCREMENT PRIMARY KEY,userName VARCHAR(255),giftName VARCHAR(255),giftNum INT,points INT,teacher VARCHAR(255),sendDate DATETIME,…

《蜜蜂路线》

题目背景 无 题目描述 一只蜜蜂在下图所示的数字蜂房上爬动,已知它只能从标号小的蜂房爬到标号大的相邻蜂房,现在问你&#xff1a;蜜蜂从蜂房 mm 开始爬到蜂房 nn&#xff0c;m<nm<n&#xff0c;有多少种爬行路线&#xff1f;&#xff08;备注&#xff1a;题面有误&am…