12

我将在一个庞大的代码库(18000 多个 Java 类)中重构某些部分。目标是能够将较低层提取为独立的库,以便在当前使用此代码库副本的其他项目中重用。特别是有一部分值得重构为独立于业务逻辑的框架。最终我希望代码有一个干净的架构分层。

我使用名为 Structure 101 for java 的工具查看了代码,发现了很多(!)架构分层问题,其中低层引用了上层。

我不想简单地开始弄乱代码,而是尝试提出一个合理的策略来解决这个问题。我应该记住什么?

我正在考虑至少采取一些小步骤。我也在考虑进行单元测试,但这需要创建它们,因为没有。

对此有什么想法吗?

4

9 回答 9

7

您还应该看看 Michael Feathers 的使用遗留代码:

http://www.amazon.com/Working-Effectively-Legacy-Robert-Martin/dp/0131177052/ref=sr_1_1?ie=UTF8&s=books&qid=1242430219&sr=8-1

我认为你可以用来促进这一点的最重要的事情之一是测试,以确保在重构​​/拉出到单独的模块后一切仍然有效。通过引入一个持续集成系统来添加这一点,该系统会在您签入某些内容时运行您的测试。

于 2009-05-16T00:10:42.243 回答
5

18,000 个班级真的正在走向“巨大”的结局。这会给您带来明显的问题,包括构建/编译时间以及启动 ide 时计算机冒烟。

我的第一个假设是,对于这么多类,有很多重复的通用功能,可能还有未使用的类,甚至可能是子系统。我希望这是因为当事情变得那么大时,开发人员越来越有可能不了解整个系统,或者不知道那些 Util 函数在哪里,并且发现编写一个新函数更容易。寻找要删除的冗余将有助于简化。

另一个可能的冗余来源是无用的深层类层次结构,或者一堆毫无意义的接口(一个例子——我工作的地方有一个大约 50 个类的目录,大多数 > 1000 行(不是我的,不是我的!)。这些实现中的每一个一个接口,它只不过是它自己的方法骨架。这些接口没有其他实现。所有 50 个可以毫无问题地删除)。还有一些开发人员刚刚发现了面向对象并且非常热衷于它——你知道的,扩展了 5 个抽象类和 3 个接口链的单个具体实现。

除此之外,我会尝试获取一段代码(最多几百个类)并将它们移动到一个子项目,然后我将其作为 jar 链接到主项目。然后,您可以稍微平静地工作,并有合理的希望能够理解整个事情-这也有心理方面的影响-如果您觉得自己在工作,就没有做好工作的动力处理一个巨大的、难以理解的混乱,而不是在你自己完全理解的干净子项目上工作。

于 2009-05-16T00:05:31.723 回答
4

第一件事:祝你好运,你会需要它。这可能是你遇到的一项巨大的工作。这对我来说听起来很熟悉;我过去做过类似的事情。

需要考虑的一件事;在你开始重构之前,我真的强烈考虑建立一个广泛的测试框架。原因是这样的:通过良好的单元测试和回归测试,您可以开始进行更改,而不必过多担心破坏现有功能。(也就是说,总是有一个问题,但是......)

也就是说:我会考虑切掉不同的“垂直”功能切片,看看你是否可以为它们编写不同的单元和集成测试;完成后,我会加入并开始进行重构。虽然一开始它可能非常小,但是仅仅隔离垂直部分的功能,然后为它编写集成和单元测试代码的过程会让你对现有的代码库有很多经验。如果你一开始设法让它变得更好一点,那么你就领先了那么多。

完成之后,开始查看可能更大的功能块进行重构。如果无法获得干净的功能块来重构,我会开始研究小块;如果你能找到一小块(有时非常小)的代码来提取、单元测试和重构,那么你就在前进。有时这可能看起来非常非常缓慢,如果你有一个非常大的项目,它会,但你会有所作为。

但总的来说,首先考虑进行测试以确认预期的功能。一旦这些测试到位,你就可以自信地重构(不是完全自信,但总比没有好)你没有破坏任何东西。从小处着手,并建立在现有代码库之外的技术之上。这是一个漫长的过程,但你最终会到达那里,并且代码库会更好。

于 2009-05-15T23:29:36.773 回答
3

在我的脑海中:

  • 识别功能域,这将有助于在庞大的代码库中定义应用程序。
  • 反过来,确定这些应用程序之间的依赖关系:底部的那些(所有其他应用程序都使用)通常是技术框架或库。

  • 创建场景测试(而不是单元测试,在这个阶段太多“本地化”)来识别重要的运行时过程及其输出。场景测试更关注集成,也可以用于非回归测试。

  • 准备一个当前的生产环境并确定当前的错误,因为当您开始重构时将需要并行运行(以确保您仍然保持相同的功能正常工作),并且您不希望您的并行运行是 100% 兼容的(因为这意味着你已经成功地重现了这些错误!)

  • 确保创建适当的合并工作流来管理代表不同(并且可能是并行的)重构工作的不同分支。

于 2009-05-15T23:22:01.643 回答
1

如果您要提取类组并将它们变成独立的库,请确定组的成员并开始将它们变成一个有凝聚力的整体,限制它们与外部世界的交互。尽可能减少依赖。完成后,拉出那个组,把它变成一个库,重新插入库,然后开始一个新的组。你清理的垃圾越多,就越容易理解剩下的东西。

于 2009-05-15T23:25:32.740 回答
1

尝试使您的依赖关系树尽可能平坦。

做到这一点的一种好方法是使用反向依赖,其他代码可以依赖于接口/服务,但不依赖于该服务的提供者。这对我们帮助很大。

于 2009-05-15T23:45:13.537 回答
0

只是一些想法:

  • 寻找常见的设计模式——尝试查看哪些类用于核心工作,哪些是工厂,哪些是外观或适配器。
  • 将代码拆分为依赖于或共享应用程序状态的类组。
  • 确定哪些类具有持久对象,以及那些在数据库中序列化的对象(应该是最容易隔离的,提供最干净的事务接口,然后可以在项目之间移植)
于 2009-05-15T23:38:35.410 回答
0

我的想法是,在设置好测试基础设施之后,如果可以将测试代码的共同特征抽象出来,你可以为测试用例编写代码生成工具,也许静态代码分析工具可以作为可视化工具之外的附加组件。对不起,这是一个想法。我什至无法命名这些工具。

于 2009-05-16T00:32:08.760 回答
0

我与我正在处理的代码库处于类似的位置。swing UI 和业务逻辑之间非常紧密的集成。重构是一个微妙而耗时的项目。

我强烈推荐 Martin Fowler 的Refactoring。这是我发现的最重要的工具,它帮助我改进了处理糟糕代码库的方法。他概述了重构任何代码的逻辑和直接的过程。从多次这样做的人那里阅读它会有所帮助。

于 2009-05-16T00:32:59.083 回答