Are You Still Debugging?

The following text is a partial translation of the original English article, performed by ChatGPT (gpt-3.5-turbo) and this Jekyll plugin:

【调试】是“一个交互式运行程序/方法的过程,在每个语句后打断执行流程并显示…”。简而言之,这是一种非常有用的技术…对于一个糟糕的程序员来说。或者一个仍在使用C语言编写过程式代码的老程序员。面向对象的程序员从不调试他们的代码—他们编写单元测试。我的观点是,单元测试是一种完全替代调试的技术。如果需要调试,那么设计是糟糕的

假设我是一个糟糕的命令式过程式程序员,这是我的Java代码:

这个静态实用方法读取文件内容,然后找出其中所有唯一的单词。非常简单。然而,如果它不起作用,我们该怎么办?比方说这是文件的内容:

从中,我们得到了以下单词列表:

现在对我来说看起来不对劲…那么下一步是什么?要么文件读取工作不正确,要么分割出现问题。让我们来调试,对吧?我们通过输入提供一个文件,逐步进行跟踪和观察变量。我们会找到错误并修复它。但是当类似的问题出现时,我们将不得不再次进行调试!这就是单元测试应该防止的情况。

我们应该创建一次单元测试,其中复现了问题。然后我们修复问题并确保测试通过。这就是我们节约解决问题投资的方式。我们不会再次修复它,因为它不会再次发生。我们的测试将防止它再次发生。

然而,所有这一切只有在创建单元测试很容易的情况下才有效。如果它很困难,我会懒得去做它。我只会调试和修复问题。在这个特定的例子中,创建一个测试是一个相当昂贵的过程。我的意思是单元测试的复杂性将会很高。我们必须创建一个临时文件,填充数据,运行该方法,并检查结果。为了找出发生了什么以及错误在哪里,我必须创建一些测试。为了避免代码重复,我还必须创建一些辅助工具来帮助我创建临时文件并填充数据。这是很多工作。嗯,也许不算“很多”,但肯定比几分钟的调试要多。

因此,如果你认为调试更快更容易,要考虑一下你代码的质量。我敢打赌它有很多重构的机会,就像上面的例子中的代码一样。这是我会如何修改它的方式。首先,我会将它转换成一个类,因为实用的静态方法是一个不好的实践:

看起来已经好多了,但复杂性仍然存在。接下来,我会将其分解成更小的类别:

你现在觉得怎么样?为 Words 类编写一个测试是一项相当简单的任务:

那花了多少时间?不到一分钟。我们不需要创建临时文件并加载数据,因为Words类不处理文件。它只是解析传入的字符串并找出其中的唯一单词。现在很容易修复,因为测试很小,我们可以轻松地创建更多的测试;例如:

我的观点是,当编写单元测试所需的时间明显超过点击“跟踪进入/跟踪退出”按钮所需的时间时,调试是必要的。这是合乎逻辑的。我们都是懒惰的,希望快速而简单的解决方案。但调试会浪费时间和能量。它帮助我们找到问题,但不能防止它们再次出现。

当我们的代码是过程化和算法化的时候,需要调试—当代码完全是关于如何实现目标而不是什么目标的时候。再看一下上面的例子。第一个静态方法完全是关于我们如何读取文件、解析文件并找到单词。它甚至被命名为readWords()(一个动词)。相反,第二个例子是关于将要实现的目标。它可以是文件的Text或文本的Words(都是名词)。

我相信在清晰的面向对象编程中没有调试的位置。只有单元测试!

Translated by ChatGPT gpt-3.5-turbo/42 on 2023-11-22 at 10:01

sixnines availability badge   GitHub stars