我有一些研究代码是真正的老鼠窝,到处都是重复的代码,显然需要重构。然而,代码库正在演变,因为我提出了新的主题变体并将它们融入代码库。我推迟重构这么久的原因是因为我觉得我花了几天时间提出好的抽象,看看什么样的设计模式适合哪里等等,我想尝试一些新的不可预见的想法使我的抽象完全不充分。换句话说,由于代码的发展速度,我真的不知道抽象线属于哪里,即使不乏(近似)重复,而且代码的一般混乱使得向其中添加东西成为现实痛。
9 回答
不要花这么长时间重构!
当您要对一段代码进行更改时,请考虑重构它以使更改更容易。
进行更改后,再次重构以清除该更改造成的损害。
在这两种情况下,将重构缩小并快速完成,然后继续。
您不必始终保持代码的原始状态,但请记住,如果您有良好的代码可以工作(当然,如果您有良好的单元测试),那么快速运行会更容易。
测试驱动开发:
红色,绿色,重构。冲洗,重复。
由于它是每个周期中的步骤之一,您会注意到通常会发生很多次要的重构。这就是它应该的方式。
你的情况我很熟悉。在进行调查性编码时,您通常不知道“正确”的抽象是什么,并且正如您所说,它会随着每个新想法而改变。
其他海报建议:
- 持续的小重构,有助于避免陷入鼠窝的境地
- 测试驱动开发,这有助于找到好的、可重用的抽象。重要的是要注意,TDD 与其说是测试,不如说是做好的设计!
但是,对于调查研究代码还有另一种策略:原型。这似乎是你目前正在做的事情:尽快编码来证明一个概念。这没有什么问题,但原型应该总是被扔掉。调整它,直到您拥有所有必要的输入和知识,然后丢弃代码 并重新开始 TDD 和持续重构,以及所有其他“正确地做事”策略。
不要保留任何代码。不要复制粘贴任何东西。不要回头提及它。用你的新知识重新开始。
一次清理一点代码。总是当你接触一个班级时,尽量让班级保持在你接触之前的清洁状态(“童子军规则”)。重构最好在非常小的步骤中完成,但非常频繁。
像重命名一些变量、拆分方法等只需要几秒钟或几分钟。大型重构,例如拆分或加入类,可能需要一两个小时(并且您以小步骤进行,以便所有测试至少每五分钟通过一次 - 否则您已进入重构地狱,您应该恢复到最后一次已知的工作状态)。如果你需要几天或几周的时间来重构某些东西,那么它就不再是“重构”了——它更像是重写。
关于这个主题的一篇文章:http: //blog.objectmentor.com/articles/2007/07/20/whats-your-unit-of-measure
至少把它放在像 Git 这样的分布式 SCM 中,这样当你破坏某些重构时,你可以分时地倒转时间以找到更改之前的提交,并且能够处理更改并将它们提交到分支中而不会干扰其他工作.
Gits 分支合并非常适合这样的事情,如果 2 个人并行进行了不兼容的更改,您将很容易知道,而不必担心其余代码。
由于上述原因,我还将在存储库中创建一个单独的分支,仅用于重构代码,并定期更新。这样,其他人不仅不会干扰您的进度,而且他们可以密切关注并查看最终会影响主分支的更改,以便他们可以先发制人地围绕这些更改进行编码。
如果你已经知道哪里有重复,你不需要几天的时间来重构它。
有时重写是唯一的选择。情况似乎如此。
CloneDR在大型源系统中查找重复的代码,包括精确的副本和接近未命中的代码,由语言语法参数化。它支持 Java、C#、COBOL、C++、PHP 和许多其他语言。
当它显示一组找到的克隆的参数化抽象时,它本质上是建议您重构代码并实现该抽象(作为方法、函数、类......)。
因此,运行 CloneDR 会获得要添加到代码中的潜在抽象列表,并通过调用抽象来替换克隆实例,从而重构代码,从而(在某种程度上)清理它。
更值得注意的是,当它显示在调用抽象所需的每个克隆站点上使用的参数绑定时,它通常显示一个错误的克隆实例,当绑定的参数在概念上不一致时很容易识别。如果参数绑定到名为 YYYY-MM-DD 的变量,其中一个是 YY-MM-DD,则“它的 4 位数年份”参数类型看起来违反了,在这种情况下,Y2K 修复已损坏。因此检查克隆绑定经常会发现错误。
这是科学计算中非常普遍的问题。减少代码大小和复杂性的一些最有效的想法需要利用假设,而科学要求您不断改变这些假设。
您所能做的就是尝试随时重构您的代码,并且尽量不要将自己写进任何角落。还要与了解不弄乱的价值的好人一起工作。