12

我在一个中等规模的团队中工作,并且经常遇到这些令人痛苦的大类文件。我的第一个倾向是用刀去攻击他们,但这通常只会让事情变得更糟,让我陷入糟糕的心态。

例如,假设您刚刚获得了一个 Windows 服务来处理。现在这个服务有一个错误,你需要弄清楚这个服务做了什么,然后才能有任何修复它的希望。你打开服务,发现有人决定只使用一个文件来处理所有事情。开始方法在那里,停止方法,定时器,所有的处理和功能。我说的是数千行代码。一百行代码以下的方法很少见。

现在假设你不能重写整个类并且这些神类只是不断弹出,处理它们的最好方法是什么?你从哪里开始?你首先尝试完成什么?你如何处理这种事情而不是只想得到所有的刺伤。

如果你有一些策略来控制你的脾气,那也是受欢迎的。

到目前为止的提示:

  1. 建立测试覆盖率
  2. 代码折叠
  3. 重组现有方法
  4. 记录发现的行为
  5. 以渐进式改进为目标

编辑:

Charles Conway 推荐了一个非常有帮助的播客。关联

Michael Feathers(播客中的那个人)从一个前提开始,他们不敢简单地将项目从源代码控制中取出,直接使用它,然后丢弃更改。我可以说我对此感到内疚。

他基本上是说拿走你想了解更多的项目,然后开始把它拆开。发现它的依赖关系,然后打破它们。跟随它走过它所到之处。

很棒的提示 使用在别处使用的大类并让它实现一个空接口。然后使用该类获取代码并让它实例化接口。这将为您提供代码中该大型类的所有依赖项的完整列表。

4

7 回答 7

9

哎哟! 听起来像我以前工作的地方。

查看有效地使用遗留代码工作。它有一些关于如何处理残暴代码的宝石。

DotNetRocks 最近做了一个关于使用遗留代码的节目。没有什么神奇的药丸可以让它起作用。

我听到的最好的建议是开始在测试中逐步包装代码。

于 2008-12-12T04:21:42.140 回答
4

这让我想起了我现在的工作以及我第一次加入的时候。他们不让我重写任何东西,因为我有同样的论点,“这些类太大而​​且写得不好!没人能理解它们,更不用说给它们添加新功能了。”

所以我要做的第一件事是确保在你想要改变的领域背后有全面的测试。至少那时您将有机会更改代码并且没有(太多)参数(希望如此)。通过测试,我的意思是通过集成或验收测试对组件进行功能测试,并确保它被 100% 覆盖。如果测试很好,那么您应该能够通过将大类拆分为较小的类、消除重复等来自信地更改代码

于 2008-12-12T04:18:09.290 回答
4

即使您无法重构文件,也要尝试重新组织它。移动方法/函数,以便它们至少在文件中进行逻辑组织。然后放入大量评论来解释每个部分。不,您还没有重写程序,但至少现在您可以正确阅读它,并且下次您必须处理该文件时,您会收到很多评论,由您编写(希望这意味着您将能够理解它们)这将帮助您处理程序。

于 2008-12-12T04:19:34.267 回答
3

代码折叠可以提供帮助。如果您可以在巨型类中移动东西并以某种合乎逻辑的方式组织它,那么您可以在各种块周围放置折叠。

隐藏一切,你又回到了 C 范式,除了折叠而不是单独的文件。

于 2008-12-12T04:18:59.977 回答
3

我也遇到过这种情况。

我个人先打印出(是的,可能是很多页)代码。然后我在不属于任何“主循环”或只是辅助函数的代码部分周围画了一个框,并确保我首先理解这些内容。原因是他们可能在课堂主体中被多次提及,很高兴知道他们在做什么

其次,我确定主要算法并使用数字和字母交替的编号系统将它们分解成各个部分(这很丑但对我来说效果很好)。例如,您可能正在查看深度为 4 个“级别”的算法的一部分,编号将是 1.b.3.e 或其他一些可怕的东西。请注意,当我说级别时,我不一定直接指控制块或范围,而是我已经确定了算法的步骤和子步骤。

然后就是阅读和重新阅读算法的问题。当你开始时,这听起来像是很多时间,但我发现这样做会培养一种自然的能力,可以同时理解大量逻辑。此外,如果您发现归因于此代码的错误,提前在纸上进行可视化分解有助于您稍后“导航”代码,因为您的脑海中已经有了它的某种地图。

如果您的老板认为您在使用某种形式的 UML 描述某事之前不理解某事,那么如果您假设子步骤级别是水平表示的不同“类”,那么 UML 序列图可能会有所帮助,并且从开始到结束是从上到下垂直表示。

于 2008-12-12T04:38:30.000 回答
3

我感觉到你的痛苦。我曾经为一个涉及在我的计算机上处​​理数字电视数据的爱好项目解决了类似的问题。硬件论坛上的一位同事编写了一个很棒的工具,用于录制节目、查看正在播放的所有内容等等。此外,他在解决实际广播信号中违反标准的错误方面做了非常重要的工作。他在线程调度方面做了惊人的工作,以确保无论如何,你都不会丢失那些实时数据包:在旧的 Pentium 上,他可以在玩 Doom 的同时同时录制四个流,并且永远不会丢失一个包。简而言之,这段代码包含了大量的知识。我希望采取一些措施并将它们合并到我自己的项目中。

I got the source code. One file, 22,000 lines of C, no abstraction. I spent hours reading it; there was all this great work, but it was all done badly. I was not able to reuse a single line or even a single idea.

I'm not sure what the moral of the story is, but if I had been forced to use this stuff at work, I would have begged permission to chip pieces off it one at a time, build unit tests for each piece, and eventually grow a new, sensible thing out of the pieces. This approach is a bit different than trying to refactor and maintain a large brick in place, but I would rather have left the legacy code untouched and tried to bring up a new system in parallel.

于 2008-12-12T05:48:39.030 回答
2

我要做的第一件事是编写一些单元测试来装箱当前的行为,假设还没有。然后,我将从需要进行更改的区域开始,并尝试清理该方法——即在引入更改之前重构工作代码。使用常见的重构技术从现有的长方法中提取和重用方法,使其更易于理解。当你提取一个方法时,寻找代码中存在类似代码的其他地方,将那个区域框起来,然后重用你刚刚提取的方法。

寻找可以分解成自己的类的“挂在一起”的方法组。为这些类应该如何工作编写一些测试,如果需要,使用现有代码作为模板构建类,然后将新类替换为现有代码,删除它们替换的方法。同样,使用您的测试来确保您没有破坏任何东西。

对现有代码进行足够的改进,以便您觉得可以以干净的方式实现新功能/修复。然后为新功能/修复编写测试并实施以通过测试。不要觉得你必须第一次解决所有问题。旨在逐步改进,但总是让代码比你发现的更好。

于 2008-12-12T04:19:55.637 回答