为了重构庞大的代码库,我应该记住啥?
Posted
技术标签:
【中文标题】为了重构庞大的代码库,我应该记住啥?【英文标题】:What should I keep in mind in order to refactor huge code base?为了重构庞大的代码库,我应该记住什么? 【发布时间】:2010-10-26 15:42:02 【问题描述】:我将在一个庞大的代码库(18000 多个 Java 类)中重构某些部分。目标是能够将较低层提取为独立的库,以便在当前使用此代码库副本的其他项目中重用。特别是有一部分值得重构为独立于业务逻辑的框架。最终我希望代码有一个干净的架构分层。
我使用名为 Structure 101 for java 的工具查看了代码,发现了很多(!)架构分层问题,其中下层引用上层。
我不想简单地开始弄乱代码,而是尝试提出一个合理的策略来解决这个问题。我应该记住什么?
我正在考虑至少采取一些小步骤。我也在考虑是否有单元测试,但这需要创建它们,因为没有。
对此有什么想法吗?
【问题讨论】:
【参考方案1】:在我的脑海中最重要的是:
识别功能域,这将有助于在庞大的代码库中定义应用程序。反过来,识别这些应用程序之间的依赖关系:位于底部的那些(由所有其他应用程序使用)通常是技术框架或库。
创建scenario testing(而不是单元测试,在这个阶段太多“本地化”)来识别重要的运行时进程及其输出。场景测试更关注集成,也可以用于非回归测试。
准备当前的生产环境并确定当前的错误,因为在开始重构时需要并行运行(以确保您仍然保持相同的功能正常工作),并且您不希望并行运行100% 兼容(因为这意味着您已成功重现错误!)
确保创建适当的merge workflow 来管理代表不同(并且可能并行)重构工作的不同分支。
【讨论】:
【参考方案2】:如果您要提取类组并将它们转变为独立的库,请确定组的成员并开始将它们转变为一个有凝聚力的整体,从而限制它们与外部世界的交互。尽可能减少依赖。完成后,拉出那个组,把它变成一个库,重新插入库,然后开始一个新的组。你清理的垃圾越多,就越容易理解剩下的东西。
【讨论】:
【参考方案3】:第一件事:祝你好运,你会需要它的。这可能是你遇到的一项巨大的工作。这对我来说听起来很熟悉;我过去做过类似的事情。
要考虑的一件事;在你开始重构之前,我真的强烈考虑建立一个广泛的测试框架。原因是这样的:通过良好的单元测试和回归测试,您可以开始进行更改,而不必过多担心破坏现有功能。 (也就是说,总是有一个问题,但是......)
也就是说:我会考虑切掉不同的“垂直”功能切片,看看您是否可以为它们编写不同的单元和集成测试;完成后,我会加入并开始进行重构。虽然一开始它可能非常小,但是仅仅隔离垂直部分的功能,然后为它编写集成和单元测试代码的过程会让你对现有的代码库有很多经验。如果你一开始设法让它变得更好一点,那么你就领先了那么多。
完成此操作后,开始查看可能需要重构的更大功能块。如果无法获得干净的功能块来重构,我会开始研究小块;如果你能找到一小块(有时非常小)的代码来提取、单元测试和重构,那么你就在前进。有时这可能看起来非常非常缓慢,如果你有一个非常大的项目,它会这样,但你会有所作为。
但一般来说,首先要考虑进行测试以确认预期的功能。一旦这些测试到位,你就可以自信地重构(不是完全自信,但总比没有好)你没有破坏任何东西。从小处着手,并建立在现有代码库之外的技术之上。这是一个漫长的过程,但你最终会到达那里,代码库会更好。
【讨论】:
【参考方案4】:只是一些想法:
寻找常见的设计模式 - 尝试查看哪些类用于核心工作,哪些是工厂,哪些是外观或适配器。 将代码拆分为依赖于或共享应用程序状态的类组。 确定哪些类具有持久性对象,以及哪些类在数据库中被序列化(这应该是最容易隔离的,提供最干净的事务接口,然后可以在项目之间移植)【讨论】:
【参考方案5】:尝试使您的依赖关系树尽可能平坦。
这样做的一个好方法是使用反向依赖,其他代码可以依赖于接口/服务,但不能依赖于该服务的提供者。这对我们帮助很大。
【讨论】:
【参考方案6】:18,000 个课程确实正走向“巨大”的结局。这会给您带来明显的问题,包括构建/编译时间以及启动 ide 时计算机冒烟。
我的第一个假设是,有这么多类,有很多重复的通用功能,可能还有未使用的类,甚至可能是子系统。我希望这是因为当事情变得那么大时,开发人员越来越有可能不了解整个系统,或者不知道那些 Util 函数在哪里,并且发现编写一个新函数更容易。寻找要删除的冗余将有助于简化。
另一个可能的冗余来源是无用的深层类层次结构,或一堆毫无意义的接口(例如 - 我工作的目录中有大约 50 个左右的类,大多数 > 1000 行(不是我的,不是我的!)。每个其中实现了一个接口,它只不过是它自己的方法骨架。这些接口没有其他实现。所有 50 个可以毫无问题地删除)。还有一些开发人员刚刚发现了面向对象并且非常热衷于它——你知道的,扩展了 5 个抽象类和 3 个接口链的单个具体实现。
除此之外,我会尝试获取一小段代码(最多几百个类)并将它们移动到一个子项目,然后我将其作为 jar 链接到主项目。然后,您可以稍微平静地工作,并有合理的希望能够理解整个事情-这也有心理方面的影响-如果您觉得自己在工作,就没有做好工作的动力处理一个巨大的、难以理解的混乱,而不是在你自己完全理解的干净子项目上工作。
【讨论】:
【参考方案7】:您还应该看看 Michael Feathers 的 Working with legacy code:
http://www.amazon.com/Working-Effectively-Legacy-Robert-Martin/dp/0131177052/ref=sr_1_1?ie=UTF8&s=books&qid=1242430219&sr=8-1
我认为您可以采取的最重要的措施之一是进行测试,以确保在重构/提取到单独的模块后一切仍然有效。通过引入一个持续集成系统来添加这一点,该系统会在您签入时运行您的测试。
【讨论】:
最重要的部分是 CI 系统,因为它允许您确保使用您正在使用的代码的所有项目仍然在您提交每次更改后构建。构建测试很困难,但它可以帮助您阐明新的层分离应该去哪里。如果您不能编写测试,那么您就不能从其他地方很好地调用它。 感谢您对本书的指点,我会研究一下。【参考方案8】:我的想法是,在设置好测试基础设施之后,如果可以将测试代码的共同特征抽象出来,你可以为测试用例编写代码生成工具,也许静态代码分析工具可以作为可视化之外的附加组件工具。对不起,这是一个想法。我什至无法命名这些工具。
【讨论】:
【参考方案9】:我与我正在处理的代码库处于类似的位置。 swing UI 和业务逻辑之间非常紧密的集成。重构是一个微妙而耗时的项目。
我强烈推荐 Martin Fowler 的 Refactoring。这是我发现的最重要的工具,它帮助我改进了处理糟糕代码库的方法。他概述了重构任何代码的逻辑和直接的过程。从多次这样做的人那里阅读它会有所帮助。
【讨论】:
以上是关于为了重构庞大的代码库,我应该记住啥?的主要内容,如果未能解决你的问题,请参考以下文章
重构此代码以更好地利用 CSharpFunctionalExtensions 库