我偶尔会很不幸地不得不对非常陈旧、没有记录和设计不佳 的代码进行修改。
进行简单的更改通常需要很长时间,因为现有代码的结构并不多,而且我真的必须阅读大量代码才能了解事情的发展方向。
我认为在这种情况下会有很大帮助的是一种工具,它可以让人们可视化代码的概述,然后甚至可以深入了解更多细节。我怀疑这样的工具很难做到正确,因为它试图找到几乎没有或没有的结构。
我想这不是一个真正的问题,而是一个沉思。我应该把它变成一个问题 -其他人如何帮助他们了解其他人的代码,好的和坏的?
我偶尔会很不幸地不得不对非常陈旧、没有记录和设计不佳 的代码进行修改。
进行简单的更改通常需要很长时间,因为现有代码的结构并不多,而且我真的必须阅读大量代码才能了解事情的发展方向。
我认为在这种情况下会有很大帮助的是一种工具,它可以让人们可视化代码的概述,然后甚至可以深入了解更多细节。我怀疑这样的工具很难做到正确,因为它试图找到几乎没有或没有的结构。
我想这不是一个真正的问题,而是一个沉思。我应该把它变成一个问题 -其他人如何帮助他们了解其他人的代码,好的和坏的?
嗯,这是一个很难的,这么多说这么少时间......
1)如果你可以运行代码,它会让生活变得更加轻松,断点(尤其是条件断点)是你的朋友。
2)纯粹主义者的方法是为已知功能编写一些单元测试,然后重构以改进代码和理解,然后重新测试。如果事情破裂,那么创建更多的单元测试 - 重复直到无聊/旧/移动到新项目
3) ReSharper 擅长显示事物在哪里被使用,例如什么在调用一个方法,它是静态的,但是一个好的开始,它有助于重构。
4) 许多 .net 事件被编码为公开的,在最好的情况下调试事件可能会很痛苦。将它们重新编码为私有并使用带有添加/删除的属性。然后,您可以使用断点来查看正在侦听的事件。
顺便说一句 - 我正在玩 .Net 空间,并且希望有一个工具来帮助做这种事情,比如 Joel 有没有人知道一个好的动态代码审查工具?
过去,我被要求拥有一些 NASTY 代码的所有权——工作和“游戏”。
我接手代码的大多数业余爱好者只是在某种程度上改进了代码,以便在几次迭代中完成他们需要的工作。库 A 调用 B、调用回 A、调用 C、调用 B 等等总是乱七八糟的。很多时候他们会使用线程而不是关键部分。
我发现获取代码句柄的最佳/唯一方法是从操作系统入口点 [main()] 开始并构建我自己的调用堆栈图来显示调用树。您实际上并不需要在一开始就构建一棵完整的树。只需跟踪您在每个阶段正在处理的部分,您就会对能够运行它的事情有足够的把握。
最重要的是,使用你能找到的最大的一片死树和一支笔。将所有内容都摆在您面前,这样您就不必在屏幕或页面上来回跳转,让生活变得如此简单。
编辑:有很多关于编码标准的讨论......他们只会让糟糕的代码看起来与好的代码一致(并且通常更难发现)。编码标准并不总是使维护代码更容易。
我定期这样做。并开发了一些工具和技巧。
这样做的问题是,在大多数公司中,您都会因结果而受到赞赏。这就是为什么一些程序员快速编写糟糕的代码并转移到不同的项目的原因。所以你只剩下垃圾了,你的老板将你缓慢的进步与快速而肮脏的家伙进行比较。(幸运的是,我现在的雇主不同)。
我一般使用组件使用的各种关键方式的 UML 序列图。我不知道有什么工具可以自动生成它们,但许多 UML 工具(例如 BoUML 和 EA Sparx)可以从源代码创建类/操作,从而节省一些输入。
有关通过单元测试掌握旧版应用程序的建议,请参阅单元测试旧版 ASP.NET Webforms 应用程序。
有很多类似的问题和答案。这是搜索https://stackoverflow.com/search?q=unit+test+legacy
关键是,如果您正在为遗产编写单元测试,那么了解遗产可能是最容易的。
我没有很好的工具来自动审查记录不佳/执行的代码,导致一个令人困惑/设计糟糕的程序通常会转化为一个不太有用的模型。这并不令人兴奋或立即获得回报,但我已经获得了最好的结果,我选择了一个位置并逐行跟踪程序执行,记录并添加注释,并在适用的情况下进行重构。
在许多情况下,一个好的 IDE(EMACS 或 Eclipse)会有所帮助。同样在 UNIX 平台上,有一些用于交叉引用(etags、ctags)或检查(lint)或 gcc 的工具打开了许多警告选项。
首先,在尝试理解一个函数/方法之前,我会对其进行一些重构以适应您的编码约定(空格、大括号、缩进),如果它们似乎是错误的,则删除大部分注释。
然后我会重构和注释你理解的部分,并尝试在整个源代码树中查找/grep 这些部分并在那里重构它们。
随着时间的推移,你会得到一个更好的代码,你喜欢使用它。
我个人做了很多图表的绘制,并弄清楚结构的骨骼。
一时的流行(而且可能是完全正确的)让我编写单元测试来测试我的断言,并为我对系统所做的更改建立一个安全网。
一旦我对系统的功能感到满意,我会尝试以最合理的方式修复错误,并希望我的安全网接近完成。
然而,这只是我。;)
我实际上一直在使用 ReSharper 的重构功能来帮助我处理我最近继承的一堆项目。因此,为了找出另一个程序员的结构非常糟糕、未记录的代码,我实际上是从重构它开始的。
清理代码、正确重命名方法、类和命名空间,提取方法都是结构性变化,可以阐明一段代码应该做什么。重构您不“知道”的代码可能听起来违反直觉,但请相信我,ReSharper 确实允许您这样做。以红鲱鱼死代码问题为例。您会在类中看到一个方法,或者可能是一个奇怪命名的变量。您可以从尝试查找用法开始,或者进行文本搜索,但 ReSharper 实际上会检测死代码并将其着色为灰色。一旦你打开一个文件,你就会看到灰色的和带有滚动条标志的文件,这在过去会让人困惑。
还有许多其他技巧,可能还有许多其他工具可以做类似的事情,但我是一个 ReSharper 瘾君子。
干杯。
从用户的角度深入了解软件。通过研究用户界面并与之交互,可以了解很多关于底层结构的信息。
能够在可怜的东西上乱涂乱画对我来说是最有用的方法。通常我会在尝试制作基本的代码结构图时出现很多“嗯,这很有趣……”,最终结果证明它们比图本身更有用。自动化工具可能比我认为的更有帮助,但找到这些有趣的部分的价值超过了快速生成图表的价值。
对于图表,我主要寻找数据的去向。它从哪里进来,它在哪里结束,以及它在途中经历了什么。一般来说,数据所发生的事情似乎会给整体布局留下一个很好的印象,如果我要重写的话,还有一些骨头要回来。
当我处理遗留代码时,我不会试图理解整个系统。这将导致复杂性超载和随后的大脑爆炸。
相反,我采用系统的一个单一功能,并尝试从头到尾完全理解它是如何工作的。我通常会调试代码,从 UI 代码中我可以找到特定功能的点开始(因为这通常是我最初能够找到的唯一内容)。然后我将在 GUI 中执行一些操作,并在代码中一直向下钻取到数据库中,然后再备份。这通常会导致对系统的至少一个特征有一个完整的理解,有时也会让我们深入了解系统的其他部分。
一旦我了解了正在调用哪些函数以及涉及哪些存储过程、表和视图,然后我会搜索代码以找出应用程序的哪些其他部分依赖于这些相同的函数/过程。这就是我如何确定我将要进行的更改是否会破坏系统中的其他任何内容。
有时尝试绘制数据库和/或代码结构图也很有用,但有时它太糟糕或太复杂了,最好忽略整个系统,只关注你需要的部分改变。
我的大问题是我(目前)有非常大的系统需要在相当短的时间内理解(我在这一点上同情合同开发人员)并且没有很多这样做的经验(以前有幸成为从头开始设计。)
我使用的一种方法是尝试理解变量、方法、类等命名的含义。这很有用,因为它(希望越来越多)嵌入了原子级别的一系列思想的高级视图。
我这样说是因为通常开发人员会有意义地命名他们的元素(用他们认为是什么)并提供对其预期功能的洞察力。诚然,这是有缺陷的,如果开发人员对其程序的理解有缺陷,则术语或(通常情况下,恕我直言)试图听起来很聪明。有多少开发人员第一次看到关键字或类名,然后才在字典中查找该术语?
这完全取决于您的公司正在使用的标准和编码规则。
如果每个人的代码风格不同,那么很难维护其他程序员的代码等等,如果你决定使用什么标准有一些规则,一切都会好起来的:) 注意:你不必做很多规则,因为人们应该有可能以他们喜欢的风格进行编码,否则你会感到非常惊讶。