代码之家  ›  专栏  ›  技术社区  ›  Ricket

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

  •  38
  • Ricket  · 技术社区  · 14 年前

    我有一大堆代码。诚然,一年前我自己写的。它没有很好的注释,但也不是很复杂,所以我可以理解它——只是还不足以知道从哪里开始重构它。

    我违反了过去一年里读到的每一条规则。有多个类具有多个职责,有间接访问(我忘记了这个术语-类似于 foo.bar.doSomething() ,就像我说的,这不是很好的评论。最重要的是,这是一个游戏的开始,所以图形与数据耦合,或者在我试图将图形和数据分离的地方,我生成了数据 public 为了使图形能够访问所需的数据…

    真是一团糟!我从哪里开始?你怎么开始这样的事情?

    我目前的方法是将变量转换为私有变量,然后重构中断的部分,但这似乎还不够。请建议其他的策略,涉过这混乱,把它变成干净的东西,以便我可以继续我离开的地方!


    两天后更新: 我一直在画我的类中类似UML的图表,并且一路上收获了一些“低挂起的果实”。我甚至发现了一些新功能的开始代码,但是当我试图精简所有内容时,我已经能够删除这些代码,使项目看起来更干净。我可能会在操纵我的测试用例之前尽可能地重构(当然,只有100%确定不会影响功能的东西!),以便在更改功能时不必重构测试用例。(你认为我做得对吗?或者在你看来,对我来说,先把测试写下来更容易吗?)

    请投票选出最好的答案,这样我就可以公平地评分了!你也可以把你自己的答案加入到这群人中去,还有你的空间! 我再给它一天左右的时间,然后可能把投票最高的答案标记为接受。

    感谢迄今为止所有做出回应的人!


    2010年6月25日:我发现 a blog post 直接回答这个问题的人似乎对编程有很好的理解:(如果你读了他的文章,也许不会)

    为此,当我 需要重构代码:

    1. 确定代码的目的是什么
    2. 绘制所涉及类的UML和动作图
    3. 选购合适的设计图案
    4. 为当前类和方法确定更清晰的名称
    14 回复  |  直到 11 年前
        1
  •  19
  •   John    14 年前

    拿一份马丁·福勒的 Refactoring . 对于如何分解重构问题,它有一些很好的建议。大约75%的书是你可以做的小食谱式重构步骤。它还提倡在每一步之后运行自动单元测试,以证明代码仍然有效。

    至于一个开始的地方,我会坐下来为你的程序画一个高层次的架构。您不必对详细的UML模型产生幻想,但是一些基本的UML并不是一个坏主意。您需要了解主要部分是如何组合在一起的,这样您就可以直观地看到您的脱钩将在哪里发生。仅仅是一些基本的框图中的一两页就可以帮助你感受到你现在所拥有的压倒性的感觉。

    如果没有某种高级别的规范或设计,您就有再次丢失的风险,最终会导致另一个无法修复的混乱。

    如果你需要从头开始,记住你永远不会真正从头开始。你有一些代码和你第一次学到的知识。但有时它确实有助于从一个空白项目开始,并在您进行的过程中拉入一些东西,而不是在一个混乱的代码库中灭火。记住不要完全扔掉旧的,用它来做它的好部分,在你走的时候把它们拉进去。

        2
  •  16
  •   Fabian Steeg    14 年前

    在不同的情况下,对我来说最重要的是单元测试:我花了几天时间为旧代码编写测试,然后就可以自信地重构了。究竟是怎样的一个不同的问题,但是有了这些测试,我就可以对代码进行真正的、实质性的更改。

        3
  •  11
  •   Cowan    14 年前

    我会根据大家对福勒的建议 重构 但是在你的具体案例中,你可能想看看迈克尔·费瑟的 Working Effectively with Legacy Code 非常适合您的情况。

    羽毛谈论 特性测试 这是单元测试,不是为了断言系统的已知行为,而是为了探索和定义现有的(不清楚的)行为——在您编写了 拥有 遗留代码,以及自己修复它,这可能不是那么重要,但是如果您的设计是草率的,那么很有可能代码的某些部分是由“魔力”工作的,它们的行为甚至对您来说都不清楚——在这种情况下,特性测试将有帮助。

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

    a brief paper 羽毛从书中丰富了一些概念,但它确实值得一探究竟。这是我最喜欢的。

        4
  •  5
  •   Thorbjørn Ravn Andersen    14 年前

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

    这适用于任何变量名和方法名。如果名称不能准确反映该对象的用途,请将其重命名为更准确的名称。这可能需要多次迭代。如果您找不到一个短的、完全准确的名称,那么这个项目就做得太多了,您有一个很好的候选代码片段需要拆分。这些名称也清楚地表明了切割的位置。

    同时,记录你的资料。什么时候回答为什么?答案不清楚是如何传达的?(作为代码)您需要添加一些文档。捕获设计决策可能是最重要的任务,因为在代码中很难做到这一点。

        5
  •  5
  •   Chris Cooper    11 年前

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

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

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

    令人惊讶的是,正确地重新制作程序,使其能够像以前那样做,只是更加“干净”。;)

    正如其他人提到的, 单元测试是你最好的朋友! 它们可以帮助您确保重构工作正常,如果您从“零开始”,那么现在正是编写它们的最佳时机。

        6
  •  4
  •   Steve B.    14 年前

    与许多面临这个问题的人相比,您的处境要好得多,因为您了解代码应该做什么。

    正如您所做的,将变量从共享范围中去掉是一个很好的开始,因为您要划分职责。最终,您希望每个类表达一个单一的责任。您可能会看到的其他一些事情:

    • 重构的简单目标是在许多地方和长方法中重复的代码。
    • 如果您是通过静态初始化的单例或更糟的全局状态来管理应用程序状态,那么可以考虑将其移动到托管初始化系统(即依赖项注入框架,如Spring或Guice),或者至少确保初始化不会与其他代码纠缠。
    • 集中和标准化您访问外部资源的方式,特别是当您有文件位置或URL等硬编码的情况下。
        7
  •  4
  •   duffymo    14 年前

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

    单元测试的想法也是关键。您将需要一组大型的、整体的事务,这些事务将为您提供代码的整体行为。

    一旦拥有了这些,就开始为类和较小的包创建单元测试。编写测试以演示正确的行为、进行更改,并重新运行测试以演示您没有破坏所有东西。

    跟踪代码覆盖率。你会想达到70%或更高。对于您更改的类,在您进行更改之前,您希望它们达到70%或更好。

    随着时间的推移建立起安全网,您将能够自信地重构。

        8
  •  3
  •   whaley    14 年前

    非常缓慢:D

    不,说真的…一步一步走。例如,只有当某个东西影响或帮助您编写当前正在处理的bug/特性时,才重构它,并且只做这些。在你重构之前 织补 当然,您已经准备好了某种在每个构建上运行的自动化测试,这些测试将实际测试您正在编写/重构的内容。即使您没有单元测试,为正在编写的所有新的和修改过的代码添加它们也不会太迟。随着时间的推移,您的代码库将以每天或每周的小增量变得更好,而不是更糟——所有这些都不需要您做大量的更改。

    在我个人的观点和经验中,仅仅为了重构而一起重构(遗留的)代码库是不值得的。在这些情况下,最好从零开始,然后从头再来(而且很少有机会做这样的事情)。因此,只需重构增量就可以了。

        9
  •  3
  •   sal    14 年前

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

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

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

        10
  •  2
  •   Rupeshit    14 年前

    我认为你应该使用Eclipse作为一个IDE,因为它有很多插件,而且是免费的。你现在应该遵循MVC模式,是的,必须使用JUnit编写测试用例。Eclipse也有JUnit的插件,它也提供了代码重构工具,这样可以减少你的一些工作。记住,编写代码并不重要。最重要的是写干净的代码,所以现在到处都写评论,这样不仅你,还有任何其他人在读代码的时候都会觉得他在读一篇文章。

        11
  •  2
  •   Carl Manaster    14 年前

    Refactor the low-hanging fruit. 轻咬那些容易的部分,当你这样做的时候,那些难的部分会变得容易一点。当没有任何需要重构的部分时,您就完成了。

    您可能会发现最有用的重构是 Rename Method (甚至更为琐碎的重新命名,如字段、变量和参数) Extract Method Extract Class . 对于您执行的每个重构,编写必要的单元测试以确保重构的安全性,并在每次重构之后运行整个单元测试套件。在没有测试的情况下,依赖于IDE的自动重构是很有吸引力的,而且,让我们诚实地说,也是非常安全的。但是,这是一个很好的实践,并且在将来将功能添加到项目中时,让测试加入进来是很好的做法。

        12
  •  2
  •   brainjam    14 年前

    你可能想看看马丁·福勒的书 Refactoring . 这是一本普及了这个术语和技术的书(我在上他的课程时的想法是:“我一直在做很多这样的事情,我不知道它有个名字”)。链接引用:

    重构是一种受控技术 用于改进 现有代码库。其本质是 应用一系列小的 行为保持转换, 每一个都“太小了,不值得 “做”。但是累积效应 每一个转换都是 相当重要。把它们放进去 小步降低风险 引入错误。你也避免 当你在的时候系统坏了 进行重组-其中 允许您逐步重构 系统超过 时间。

    正如其他人所指出的,单元测试将允许您自信地重构。从减少代码重复开始。这本书会给你很多其他的见解。

    这里是 a catalog of refactorings .

        13
  •  1
  •   Avram    14 年前

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

        14
  •  -2
  •   Chris    14 年前

    扔掉它,重新建造。