学习遗留Java系统

时间:2009-02-09 00:33:45

标签: java legacy maintenance

我的任务是维护和重构遗留Java系统。我目前做C#和.NET,虽然我熟悉Java。

旧系统使用RMI,一种客户端/服务器架构,专为1.4 JVM而设计。它用于UI(据我所知),Swing和AWT。

我的问题是:与我刚刚提交的代码库达成协议的最佳方法是什么?我正在考虑屏幕的流程图,定义RMI调用之间的边界,以及编写单元测试(对于可测试的位)。

当你交出一个不熟悉的代码库时,你会怎么做?

谢谢!

-Jarrod

10 个答案:

答案 0 :(得分:5)

我使用的任何新代码所做的第一件事就是查看现有的单元测试。编写新测试通常是第二件事。

答案 1 :(得分:4)

这在很大程度上取决于代码库的质量。实际上,你可以更狭隘地定义它 - 它取决于代码的清晰程度,以及它的评论程度(我将其写成一个曾经继承了遗留下来的遗留代码库的人)。

代码库越糟糕,你需要从外部推断它的功能就越多 - 如果你无法弄清楚函数在做什么,你至少可以弄清楚GUI是什么似乎暗示它正在做。拥有RMI接口可能是一件幸事,因为它为您提供了GUI需要看到的简化图片 - 从远程接口抽象是一个非常好的主意。

如果代码库真的很差,单元测试不太可能对你有帮助,因为你可能正在测试一些错误的东西,即使是正确实现的也是如此。我继承的最后一件事的一个例子:(故意删除了名称和描述 - 在任何情况下它们对原文都没有帮助)

 public static int[] a (List<int[]> input){  

   ... lots of opaque rubbish
   return b(input);
 }

 public static List<int[]> b{ List<int[]> input)
 {
   ... more crap....
   return some horribly mangled list of int[];
 }

事实证明, a 完成的是按照第二个元素的值对数组进行排序。在这种情况下,测试 b 以正常运行将毫无用处。

建议帮助您保持理智:

  • 删除明显未使用的代码。
  • 尝试分解任何“神奇的数字”
  • 找到任何硬编码文件,路径或资源引用,并将它们分解为属性,或任何其他中央,通用的方式来处理它们。
  • 试图确定应用程序是否真的按照预期的方式运行 - 对于某些非常复杂的功能而言,你不能做出头脑或尾巴实际代表用户感觉“从未真正正常工作的东西”并不罕见”
  • 查看首次设计时的任何原始文档或规格。
  • 由于您提到1.4,因此通用化地图可以阐明传递的对象类型。如果你有一个由HashMap支持的对象,以及它如何被填充是一个谜,它可以真正简化你的生活,以确定它实际上是一个字符串到整数的映射 - 这通常比它实际上认为更容易弄清楚要做。

当然,如果代码库编写得很好,大部分都是不必要的,但在任何情况下你的工作都会轻松得多。祝你好运。

答案 2 :(得分:2)

有一件事可以帮助我处理对我来说很陌生的代码 - 这对于编写良好的代码来说要少得多 - 就是大量重构它一两天然后放弃我的所有更改。这个过程可以帮助我理解代码的作用;使用代码可以帮助我理解它。它也开始教我代码的哪些部分是脆弱的。

如果您有机会迁移到较新版本的Java,那么对所有集合进行泛化将有助于了解传递的数据类型。

当然,我是在测试实验室中安装软件并稍微使用它来了解它的作用后这样做的。

编辑:考虑我的答案,也很有用的是启用所有诊断跟踪和日志记录,使用系统,然后研究日志。如果存在通信协议跟踪,那么查看此跟踪将有助于理解代码使用的通信协议,可能使用相同测试的Wireshark跟踪。

另一个有用的迁移是从旧的并发库迁移到新的Java 5(和6)并发库。这将有助于您了解线程的位置以及线程何时启动以及何时关闭。

当然,对于不熟悉的代码库中的任何代码更改,我认为已经进行了适当的测试以确保没有任何损坏!但是,为了平衡这一点,我了解到在重构了编写错误的代码之后,新引入的错误通常比重构之前存在的错误更容易找到。

答案 3 :(得分:1)

接收新代码时,我要做的第一件事就是“尝试让它成功”!我的意思是:

  • 首先安装它(根据安装 如果有的话说明)

  • 然后熟悉它(通过阅读用户手册并运行一些重要的用例)

  • 然后进行一些逆向工程以找出主要层。类和依赖项

  • 然后我可以考虑编写新的测试用例(首先是在接受级别,这对以后的回归测试很有用)

  • 然后我给自己一些关于添加此功能的“挑战”(即使不是必需的)或改进现有功能的性能:这是挖掘现有代码库的好方法

    < / LI>
  • 当然,如果有一些已知的待处理错误/ RFE,我会先处理这些

在您的具体情况下,我还会尝试记录RMI调用,执行什么调用或调用序列,并在可能的情况下将其与使用级功能相关联。

此外(实际应该是第一个),要知道的一件重要事情是该系统的主要目标(为什么您的客户有这个系统?)。了解目标并牢记这些目标将避免您在维护代码的同时远离这些目标。

答案 4 :(得分:1)

Mike Hill参加了一次演讲,他谈到了他用来处理糟糕遗留代码的过程。他称之为“微型测试”,它基本上将你要测试的每个东西(在它自己的函数或小对象中)隔离开来,并围绕它编写测试。这些测试并不意味着在商业意义上断言 - 它们只是意味着在执行测试行之后捕获系统所处的状态。通常,他会断言变量具有调试器中的值。当然,这些测试最初会通过,但是在他编写了足够多的测试之后,他会有效地获得系统的快照。

一旦这些测试到位,他就可以开始重构那段代码,试图弄清楚它想要做什么。他可以安全地这样做,因为如果他改变了功能(或物体)的表现方式,他的“微观测试”就会失败。虽然这意味着他无法进行大的设计更改,但他可以消除很多噪音,他可以了解系统正在做什么。一旦他清楚地知道代码实现了什么,他就可以开始改进它,发现错误等等。

这方面没有太多免费资源,这就是我不提供链接的原因。希望我已经设法描述足以给你一些想法。

答案 5 :(得分:1)

经验告诉我,在学习遗留系统时,您有三个主要目标。

  • 了解代码应该执行的操作
  • 了解它是如何做到的
  • (至关重要)了解它为什么会这样做

这三个部分都非常重要,有一些技巧可以帮助你开始。

首先,抵制诱导只需按住Ctrl键(或IDE使用的任何内容)绕过代码来理解所有内容。你可能无法以这种方式在脑海中保持一切,特别是当每一行强迫你查看其他多个类以了解它是什么时。

尽可能阅读文档;它通常可以帮助您快速获得构建其后所有内容的心理框架。

尽可能运行测试用例。

不要害怕问一个知道你是否有问题的人。当然,你不应该浪费其他员工的时间来进行无聊的查询,但是如果有些事你根本就不理解(对于更多的概念性问题尤其如此,“实现这一点并不是更有意义” a ___“或者其他什么东西”,在搞砸了什么并且不知道为什么之前找到答案可能是值得的。

当你最终开始阅读代码时,从逻辑“主”位置开始,然后从那里开始。不要只是从上到下,按字母顺序或任何东西阅读代码(这可能很明显)。

答案 6 :(得分:0)

带有RMI的Java 1.4并不是传统的,这在一些标准下几乎是新的!

尽你所能来熟悉事物的位置 - 单元测试,跟踪代码/调用,处理一些UML图等等。

尝试从它的一小部分开始并跟踪代码路径以熟悉事物的位置,或者为自己分配一些需要查看代码的小修补/改进。

答案 7 :(得分:0)

构建并运行它将是我的第一件事。开始了解它试图解决的问题。

也许你可以将它导入到像JUDE这样的UML工具中,并获得类如何交互的图片。

编写JUnit测试是一个很好的建议。我想知道应用程序的分层程度。如果它很难测试,也许它太耦合了。单元测试会告诉你。如果你决定进行一些重构,他们也会给你一个安全网。

JDK 1.4?它已经过了支撑生命的终点。我还想看看代码是否会在JDK 5下构建和运行,最好是JDK 6.也许你可以把一些JUnit测试打到JMeter中,然后用5个同时用户做一个快速穷人的负载测试。

如果您有数据模型,请将其拉​​入ERWin并开始查看表,对象和屏幕如何组合在一起。

答案 8 :(得分:0)

当然你可以尝试一步一步。它的速度很慢,但在代码中花费了一天的时间可能会节省一周的bug搜索...

您还可以尝试清道夫搜索方法,列出每个功能并为其搜索代码......

答案 9 :(得分:0)

首先浏览代码以了解其结构。然后,在调试模式下运行应用程序并运行几次。这将向您显示流程和结构。

使用Netbeans反向设计类图。