你如何重构一个庞大的混乱代码库?

时间:2010-06-05 19:27:23

标签: language-agnostic oop refactoring

我的代码很乱。不可否认,我自己写了一年 - 一年前。它没有得到很好的评论,但它也不是很复杂,所以我可以理解它 - 只是不知道从哪个开始就重构它。

我违反了过去一年中我读过的所有规则。有些职责有多个职责,有间接访问(我忘了这个术语 - 像foo.bar.doSomething()这样的东西),就像我说的那样评论不好。最重要的是,它是游戏的开始,所以图形与数据相结合,或者我试图解耦图形和数据的地方,我制作了数据public,以便图形能够访问它需要的数据......

这是一个巨大的混乱!我从哪里开始?你会如何开始这样的事情?

我目前的方法是获取变量并将它们切换为私有,然后重构破坏的碎片,但这似乎不够。请建议其他策略来浏览这个烂摊子并将其变成干净的东西,以便我可以继续我离开的地方!


两天后更新:我一直在绘制类似UML的图表,并沿途捕捉一些“低挂水果”。我甚至发现了一些代码是新功能的开始,但是当我试图减少所有内容时,我已经能够删除这些内容并使项目感觉更清洁。在绑定我的测试用例之前,我可能会尽可能地重构(但只有100%肯定不会影响功能的东西!),所以我不必重构测试用例。改变功能。 (你认为我做得对吗,或者你认为,我更容易吸吮并先写测试吗?)

请投票选出最佳答案,以便我公平地标记!随意添加你自己的答案,还有你的空间!我会再给它一天左右,然后可能会把最高投票的答案标记为已接受。

感谢所有回复的人!


2010年6月25日:我发现a blog post直接回答了那个似乎对编程有很好掌握的人的问题:(或者,如果你阅读他的文章,可能不会:) :)

  

为此,我做了四件事   需要重构代码:

     
      
  1. 确定代码的用途
  2.   
  3. 绘制所涉及类的UML和动作图
  4.   
  5. 四处寻找合适的设计模式
  6.   
  7. 确定当前类和方法的更清晰的名称
  8.   

14 个答案:

答案 0 :(得分:19)

给自己找一份Martin Fowler的Refactoring。它有一些很好的建议来解决你的重构问题。大约75%的书是你可以做的小菜谱式重构步骤。它还提倡自动单元测试,您可以在每个步骤后运行,以证明您的代码仍然有效。

至于一个开始的地方,我会坐下来绘制你的程序的高级架构。你不必对详细的UML模型有所了解,但是一些基本的UML并不是一个坏主意。您需要全面了解主要部件如何组合在一起,以便您可以直观地看到您的去耦将要发生的位置。只需要一两页基本的方块图就可以帮助您获得现在的压倒性感觉。

如果没有某种高级别的规范或设计,你只会冒险再次迷失并最终导致另一个难以维持的混乱。

如果您需要从头开始,请记住,您从未真正从头开始。你有一些代码和你第一次获得的知识。但有时它确实有助于从一个空白项目开始并随时随地拉入内容,而不是在混乱的代码库中引发火灾。只要记住不要完全抛弃它,将其用于其良好的部件并随身携带它们。

答案 1 :(得分:16)

在不同场合对我来说最重要的是单元测试:我花了几天时间为旧代码编写测试,然后我可以自信地重构。究竟是一个不同的问题,但通过测试,我可以对代码进行真正的实质性更改。

答案 2 :(得分:11)

我将对Fowler的 Refactoring 的每个人提出建议,但在您的具体情况下,您可能需要查看Michael Feathers的 Working Effectively with Legacy Code ,这是真的适合您的情况。

Feathers谈论 Characterization Tests ,这些单元测试不是为了断言系统的已知行为,而是为了探索和定义现有的(不清楚的)行为 - 在你写完你的情况下拥有遗留代码,并自行修复,这可能不是那么重要,但如果你的设计很草率,那么很可能有部分代码通过'魔术'工作,他们的行为不是'甚至对你来说都很清楚 - 在这种情况下,表征测试会有所帮助。

本书的一个重要部分是关于在代码库中查找(或创建)接缝的讨论 - 如果您愿意,接缝是自然的“断层线”,您可以在其中闯入现有的系统开始测试它,并将其拉向更好的设计。很难解释,但值得一读。

a brief paper,Feathers从书中汲取了一些概念,但实际上非常值得追捕整个事情。这是我的最爱之一。

答案 3 :(得分:5)

你总是可以从“刮擦”开始。这并不意味着废弃它,而是从零开始,但是从一开始就尝试重新思考高级别事物,因为自从你上一次工作以来你似乎学到了很多东西。

从更高的层次开始,当您构建新的和改进的结构的脚手架时,请使用您可以重复使用的所有代码,如果您愿意阅读并制作一些代码,这可能比您想象的要多。微小的变化。

当你做出改变时,一定要严格遵守你现在所知道的所有良好做法,因为你以后会真的感谢自己。

正确地重新制作程序以完成之前所做的事情,只能更“干净”,这可能令人惊讶地令人惊讶。 ;)

正如其他人所提到的那样,单元测试是你最好的朋友!它们可以帮助你确保你的重构工作,如果你从“刮擦”开始,那么它是完美的时间。写下来。

答案 4 :(得分:5)

只是一个比你想象的更重要的额外重构:正确命名!

这适用于任何变量名称和方法名称。如果名称没有准确反映出该用途的内容,则将其重命名为更准确的内容。这可能需要多次迭代。如果您找不到简短且完全准确的名称,那么该项目会做得太多,并且您有一个非常适合需要拆分的代码段。名称还清楚地表明了切割的位置。

另外,记录你的东西。每当答案为什么?如何通过答案清楚地表达出来? (作为代码)您需要添加一些文档。捕获设计决策可能是最重要的任务,因为在代码中很难做到。

答案 5 :(得分:4)

与许多面临此问题的人相比,您处于更好的位置,因为您了解代码应该执行的操作。

将变量从共享范围中取出,正如您所做的那样,是一个很好的开始,因为您正在分配职责。最终,您希望每个班级都表达一个责任。您可能会看到的其他一些事情:

  • 重构的简单目标是在许多地方和长方法中重复的代码。
  • 如果您通过静态初始化的单例管理应用程序状态,或者更糟糕的是,一切都在与之交谈的全局状态,请考虑将其移动到托管初始化系统(即像spring或guice这样的依赖注入框架)或者至少确保初始化不会与代码的其余部分纠缠在一起。
  • 集中并标准化您访问外部资源的方式,尤其是在您有文件位置或网址硬编码的情况下。

答案 6 :(得分:4)

购买具有良好重构支持的IDE。我认为IntelliJ是最好的,但Eclipse现在也拥有它。

单元测试理念也很关键。您将需要一组大型的整体事务,它们将为您提供代码的整体行为。

完成后,开始为类和较小的包创建单元测试。编写测试以演示正确的行为,进行更改,然后重新运行测试以证明您没有破坏所有内容。

随时跟踪代码覆盖率。您需要将其工作高达70%或更高。对于您更改的课程,在进行更改之前,您将希望这些课程达到70%或更高。

随着时间的推移建立安全网,你将能够有信心地进行重构。

答案 7 :(得分:3)

非常慢:D

不认真......一步一步。例如,只有当它影响或帮助您编写您当前正在处理的当前错误/功能时才重构某些内容,并且不会更多。在重构之前make darn 确保你有一些自动化测试,可以在每个构建上运行,实际测试你正在编写/重构的内容。即使您没有单元测试,也开始为正在编写的所有新代码和修改代码添加它们永远不会太晚。随着时间的推移,您的代码库将每天或每周以较小的增量变得更好而不是更糟 - 所有这一切都没有您做出巨大的变化。

根据我个人的观点和经验,为了重构而整体重构(遗留)代码库是不值得的。在这些情况下,最好从头开始并重新做一遍(很少有人有机会做这样的事情)。因此,只需重构增量即可。

答案 8 :(得分:3)

对于Java代码,我最喜欢的第一步是运行Findbugs,然后删除所有死存储,未使用的字段,无法访问的catch块,未使用的私有方法和可能的错误。

接下来,我运行CPD以查找剪切复制粘贴代码的证据。

通过这样做可以将代码库减少5%并不罕见。它还可以避免重构从未使用过的代码。

答案 9 :(得分:2)

你可能想看看Martin Fowler的书Refactoring。这本书推广了术语和技术(我的思路在他的课程中说:“我一直在做很多这方面,我不知道它有一个名字”)。链接引用:

  

重构是一种受控制的技术   改善设计   现有代码库。它的本质是   应用一系列小   保持行为的转变,   每个“太小而不值得   做“。然而累积的影响   这些转变中的每一个都是   非常重要。通过他们做   你可以减少风险的小步骤   引入错误。你也避免   让你的系统崩溃   进行重组 - 哪个   允许你逐步重构一个   系统在很长一段时间内   时间。

正如其他人所指出的那样,单元测试可以让你自信地重构。首先是减少代码重复。这本书将为您提供许多其他见解。

这是a catalog of refactorings

答案 10 :(得分:2)

我认为你应该使用Eclipse作为IDE,因为它有许多插件并且免费。你现在应该遵循MVC模式,是的必须使用JUnit.Eclipse编写测试用例也有JUnit的插件并且它提供代码重构工具也会减少你的工作。并且永远记住编写代码并不重要,主要是编写干净的代码。所以现在在任何地方给出评论,这样不仅你,而且任何其他人在阅读时阅读代码代码他必须觉得他正在读一篇文章。

答案 11 :(得分:2)

Refactor the low-hanging fruit.蚕食简单的位,正如你那样,更难的位将开始变得更容易。当没有任何比特要重构时,你就完成了。

您可能会发现最有用的重构是Rename Method(甚至更重要的Renamings,如字段,变量和参数),Extract MethodExtract Class。对于您执行的每次重构,编写必要的单元测试以使重构安全,并在每次重构后运行整套单元测试。它是诱人的 - 而且,诚实,非常安全 - 依靠IDE的自动重构,无需测试 - 但这是一种很好的做法,并且在您为项目添加功能时将测试用于未来。 / p>

答案 12 :(得分:1)

凌乱代码的正确定义是难以维护和更改的代码 要使用更多数学定义,您可以通过代码指标工具检查您的代码 这样,你将保持已经足够好的代码,并找到非常快的错误代码 我的经验表明,这是提高代码质量的非常有效的方法。 (如果您的工具可以在每个构建或实时显示结果)

答案 13 :(得分:-2)

扔掉它,建立新的。