所以我们在项目中有这个巨大的mainmodule.cpp源文件(11000行很大吗?),每次我不得不触摸它时,我都会畏缩。
由于这个文件是如此的核心和大,它不断积累越来越多的代码,我想不出一个好方法来让它实际上开始缩小。
该文件在我们产品的几个(> 10)维护版本中被使用和积极更改,因此很难重构它。如果我“简单地”将其拆分为3个文件,那么从维护版本合并回更改将成为一场噩梦。而且,如果您拆分具有如此长而丰富历史的文件,跟踪和检查SCC历史中的旧更改突然变得非常困难。
这个文件基本上包含了我们程序的“主类”(主要的内部工作调度和协调),所以每次添加一个特性,它也会影响这个文件,每次它的增长。:-(
在这种情况下你会怎么做?关于如何在不打乱SCC工作流程的情况下将新特性移动到单独的源文件中,您有什么想法吗?
(注意:我们使用c++和Visual Studio;我们使用AccuRev作为SCC,但我认为SCC的类型在这里并不重要;我们使用Araxis Merge来做实际的文件比较和合并)
我的0.05欧分:
重新设计整个混乱的系统,考虑到技术和业务需求,将其拆分为子系统(=许多并行维护轨道,每个并行维护轨道的代码库可能不同,显然需要高可修改性等等)。
在划分子系统时,分析变化最大的地方,并将其与不变的部分分开。这应该会显示出问题所在。将最易变化的部分分离到它们自己的模块中(例如dll),这样模块API就可以保持完整,而不需要一直破坏BC。这样,如果需要,您可以为不同的维护分支部署不同版本的模块,同时保持核心不变。
重新设计很可能需要一个单独的项目,试图做一个移动的目标是行不通的。
至于源代码历史,我的意见是:为了新代码忘掉它吧。但是请将历史记录保存在某个地方,以便在需要时进行检查。我打赌你开始之后就不那么需要它了。
对于这个项目,您很可能需要得到管理层的支持。你可以用更快的开发时间、更少的bug、更容易的维护和更少的混乱来反驳。类似于“积极地使我们的关键软件资产具有未来的可靠性和维护可行性”:)
至少这是我开始解决问题的方式。
我发现这句话是你帖子中最有趣的部分:
>该文件在我们产品的几个(> 10)维护版本中被使用和积极更改,因此很难重构它
首先,我建议您使用源代码控制系统来开发这10多个支持分支的维护版本。
其次,我将创建10个分支(每个分支对应一个维护版本)。
我已经感觉到你在畏缩了!但是,要么是因为缺少特性,你的源代码控制不能满足你的情况,要么是因为它没有被正确地使用。
现在来看看您正在处理的分支——按照您认为合适的方式对其进行重构,确保不会打乱产品的其他九个分支。
我有点担心你的main()函数中有这么多。
在我编写的任何项目中,我都会使用main()只执行核心对象的初始化——比如模拟或应用程序对象——这些类才是真正的工作应该进行的地方。
我还将在main中初始化一个应用程序日志对象,以便在整个程序中全局使用。
最后,在main中,我还在预处理器块中添加了泄漏检测代码,以确保它只在DEBUG版本中启用。这是我要添加到main()的所有内容。Main()应该很短!
你这么说
>该文件基本包含了我们程序的“主类”(主要的内部工作调度和协调)
听起来这两个任务可以分成两个单独的对象——一个协调器和一个工作分派器。
当你把它们分开的时候,你可能会弄乱你的“SCC工作流”,但是听起来像严格遵守你的SCC工作流会导致软件维护问题。抛弃它,现在就不要回头,因为一旦你解决了它,你就会开始睡得很舒服。
如果您不能做出决定,那么就与您的经理进行激烈的斗争——您的应用程序需要重构——听起来很糟糕!不要接受拒绝!
我的同情-在我以前的工作中,我遇到过类似的情况,一个文件比你必须处理的文件大几倍。解决方案是:
编写代码详尽地测试程序中的函数。听起来你还没有掌握这个…
确定一些可以抽象为帮助器/实用程序类的代码。不需要很大,只是一些不是你的“主要”类的真正一部分。
重构2中确定的代码。进入一个单独的班级。
重新运行测试,以确保没有损坏。
当你有时间的时候,去2。并根据需要重复以使代码易于管理。
在第3步中构建的类。迭代可能会增加以吸收更多适合于新清除的函数的代码。
我还可以补充:
0:购买Michael Feathers关于使用遗留代码的书
不幸的是,这种类型的工作太常见了,但我的经验是,在保持工作的同时,能够使工作但可怕的代码逐渐变得不那么可怕是有很大价值的。