English 中文(简体)
如何将大型Java项目分割成较小的组件
原标题:
  • 时间:2008-11-15 20:17:58
  •  标签:

我们一直在将一个庞大的代码库分成逻辑模块。我想请教一些工具和经验,同时也想了解您在这方面的经验。

该应用程序由一个服务器WAR和几个分布在JAR中的富客户端组成。问题在于,它们都在一个大而复杂的代码库中,一个包含超过2,000个文件的源代码树。每个JAR都有一个专用类,带有一个main方法,但依赖关系的纠缠很快就会陷入困境。虽然情况并不算很糟糕,但一直遵循良好的实践并有专门任务的组件,但还需要一些改进来帮助我们的团队随着业务的发展而扩展规模。

这些模块将各自位于 Maven 项目中,由父 POM 构建。这个过程已经开始将每个 JAR/WAR 移动到自己的项目中,但很明显这只是表面工作:每个应用程序 JAR 中只有一些类,而其他所有内容则在庞大的“遗留”项目中。此外,已经有了一些单元测试和集成测试。

无论如何,我对将过于庞大和纠缠不清的代码库分解成更易管理的东西的工具、技术和一般建议很感兴趣。自由/开源是首选。

问题回答

看一下Structure 101。它非常棒,可以可视化依赖关系,展示清除结构中需要破坏的依赖关系。

我们最近完成了一个类似的任务,即一个包含> 1k个源文件的项目,其中有两个主要类需要拆分。最终我们得到了四个单独的项目,一个是用于基本实用程序类,一个用于客户端数据库内容,一个用于服务器(该项目是一个rmi-server-client应用程序),还有一个用于客户端GUI内容。我们必须将我们的项目分离出来,因为其他应用程序正在将客户端用作仅命令行,如果您无意中使用任何GUI类,您将遇到只发生在无头部署服务器上启动时出现的无头异常。

从我们的经验中记住一些事情:

  • Use an entire sprint for separating the projects (don t let other tasks interfere with the split up for you will need the the whole time of a sprint)
  • Use version control
  • Write unit tests before you move any functionality somewhere else
  • Use a continuous integration system (doesn t matter if home grown or out of the box)
  • Minimize the number of files in the current changeset (you will save yourself a lot of work when you have to undo some changes)
  • Use a dependency analysis tool all the way before moving classes (we have made good experiences with DependencyFinder)
  • Take the time to restructure the packages into reasonable per project package sets
  • Don t fear to change interfaces but have all dependent projects in the workspace so that you get all the compilation errors

两个建议:第一件你需要的是测试套件。第二个建议是采取小步骤进行工作。

如果您已经拥有强大的测试套件,那么您就处于一个良好的位置。否则,我建议您进行一些良好的高级测试(也称为:系统测试)。

高级测试的主要优势是只需进行相对较少的测试就可以获得很好的覆盖率。它们不能帮助您准确定位错误,但您实际上也不需要那样做:如果您采用小步骤工作,并确保在每次更改后运行测试,您将能够迅速检测到(无意中引入的)错误:错误的根源在代码的一小部分中,自上次运行测试以来已更改。

我会从你需要完成的各种任务开始。

我最近面对了一个相似的任务,那就是处理一个15年历史的代码库,该代码库是由一系列开发人员编写的,他们之间没有任何沟通(有些人参与项目后离开,然后招聘了另一位开发人员等等,彼此之间没有交流)。结果是一个风格和质量迥异的混杂体。

为使其正常运作,我们需要将必要的功能从装饰性内容中分离出来。例如,在这里有很多不同的字符串类,一个人花费了很多时间将COleDateTime转换为const char*并再次转换回来,但这只是额外任务的代码漂亮堆叠,而非实现主要目标(将数据导入和导出数据库)。

我们最终要做的就是确定这段代码完成的一项重大目标,然后编写该目标的基本逻辑。当我们需要完成已经完成的任务时,我们会找到它并将其封装成库调用,以便它可以独立存在。例如,一个代码块激活USB设备驱动程序以创建图像;这段代码没有被当前项目修改,但在必要时通过库调用进行调用。另一个代码块处理安全加密锁,还有一个查询远程服务器的数据。这是所有必要的代码,可以封装起来。然而,绘图代码已经建立了15年,是个疯狂的建筑,花一个月的时间以OpenGL重写比尝试弄清楚别人做了什么然后如何添加更好地利用时间。

我这里有点含糊其辞,因为我们的项目是MFC C++转换为.NET C#,但基本原则仍然适用:

  1. find the major goal
  2. identify all the little goals that make the major goal possible
  3. Isolate the already encapsulated portions of code, if any, to be used as library calls
  4. figure out the logic to piece it all together.

希望能够帮助…

继续Itay的答案,我建议阅读Michael Feathers的《与遗留代码有效地工作》(pdf)。他也建议每一步都要有测试支持。还有一本长篇版本。

Maven允许您将小项目设置为较大项目的子项目。如果您想将项目的一部分提取为单独的库以供其他项目使用,则Maven也可以让您这样做。

话虽如此,你绝对需要记录你的任务,每个较小的项目将完成什么,然后(如此之前已经说过多次)测试,测试,测试。你需要测试整个项目的测试,然后有测试单个项目部分的测试,这些部分将作为子项目。

当您开始撤销功能时,您需要额外的测试来确保您的功能一致,并且您可以将输入模拟到您的各个子项目中。





相关问题
热门标签