所以我们在项目中有这个巨大的mainmodule.cpp源文件(11000行很大吗?),每次我不得不触摸它时,我都会畏缩。
由于这个文件是如此的核心和大,它不断积累越来越多的代码,我想不出一个好方法来让它实际上开始缩小。
该文件在我们产品的几个(> 10)维护版本中被使用和积极更改,因此很难重构它。如果我“简单地”将其拆分为3个文件,那么从维护版本合并回更改将成为一场噩梦。而且,如果您拆分具有如此长而丰富历史的文件,跟踪和检查SCC历史中的旧更改突然变得非常困难。
这个文件基本上包含了我们程序的“主类”(主要的内部工作调度和协调),所以每次添加一个特性,它也会影响这个文件,每次它的增长。:-(
在这种情况下你会怎么做?关于如何在不打乱SCC工作流程的情况下将新特性移动到单独的源文件中,您有什么想法吗?
(注意:我们使用c++和Visual Studio;我们使用AccuRev作为SCC,但我认为SCC的类型在这里并不重要;我们使用Araxis Merge来做实际的文件比较和合并)
在我看来,您现在面临的是一堆代码异味。首先,主类似乎违反了开/闭原则。这听起来也像是承担了太多责任。因此,我认为代码比实际需要的更加脆弱。
虽然我可以理解您对重构后可跟踪性的担忧,但我认为该类相当难以维护和增强,而且您所做的任何更改都可能导致副作用。我假设这些的成本超过了重构类的成本。
在任何情况下,由于代码气味只会随着时间的推移而变得更糟,至少在某些时候,这些成本将超过重构的成本。根据你的描述,我认为你已经过了临界点。
重构应该分小步骤进行。如果可能的话,在重构任何东西之前添加自动测试来验证当前行为。然后挑选出独立功能的小区域,并将其提取为类型,以便委派职责。
无论如何,这听起来像是一个大项目,所以祝你好运:)
一个重要的建议:不要将重构和错误修复混合在一起。您需要的是程序的版本与以前的版本相同,只是源代码不同。
一种方法是开始将最小的函数/部分分割到它自己的文件中,然后使用头文件include(从而将main.cpp转换为#includes列表,这本身听起来有点代码味道*尽管我不是c++专家),但至少现在它被分割为文件)。
然后,您可以尝试将所有维护版本切换到“新的”main.cpp或任何您的结构。再次重申:没有其他更改或错误修复,因为跟踪这些是令人困惑的地狱。
另一件事:尽管您可能希望一次性完成整个重构,但您可能会贪多嚼不烂。也许只是选择一两个“部分”,把它们放到所有的版本中,然后为你的客户增加一些更多的价值(毕竟,重构并不会直接增加价值,所以它是一种成本,必须被证明是合理的),然后再选择另外一两个部分。
显然,这需要团队中的一些纪律来实际使用拆分文件,而不是一直向main.cpp中添加新内容,但是,尝试进行一次大规模的重构可能不是最佳的行动方案。
我不知道这是否解决了您的问题,但我猜您想要做的是将文件的内容迁移到彼此独立的更小的文件中(合计)。
我还了解到,你有大约10个不同版本的软件,你需要在不搞砸的情况下支持它们。
首先,这是不可能的简单,将解决自己在几分钟的头脑风暴。文件中链接的函数对应用程序都非常重要,简单地将它们删除并迁移到其他文件中并不能解决问题。
我认为你只有这些选择:
Don't migrate and stay with what you have. Possibly quit your job and start working on serious software with good design in addition. Extreme programming is not always the best solution if you are working on a long time project with enough funds to survive a crash or two.
Work out a layout of how you would love your file to look once it's split up. Create the necessary files and integrate them in your application. Rename the functions or overload them to take an additional parameter (maybe just a simple boolean?).
Once you have to work on your code, migrate the functions you need to work on to the new file and map the function calls of the old functions to the new functions.
You should still have your main-file this way, and still be able to see the changes that were made to it, once it comes to a specific function you know exactly when it was outsourced and so on.
Try to convince your co-workers with some good cake that workflow is overrated and that you need to rewrite some parts of the application in order to do serious business.
这并不是一个大问题的答案,而是一个具体问题的理论解决方案:
Figure out where you want to split the big file into subfiles. Put comments in some special format at each of those points.
Write a fairly trivial script that will break the file apart into subfiles at those points. (Perhaps the special comments have embedded filenames that the script can use as instructions for how to split it.) It should preserve the comments as part of the splitting.
Run the script. Delete the original file.
When you need to merge from a branch, first recreate the big file by concatenating the pieces back together, do the merge, and then re-split it.
另外,如果您想要保存SCC文件历史,我认为最好的方法是告诉您的源代码控制系统各个片段文件都是原始文件的副本。然后,它将保存该文件中保存的部分的历史,当然,它也将记录大部分被“删除”。
下面是我所想到的解决这些问题的唯一办法。所述方法的实际增益是演化的累进性。这里没有革命,否则你很快就会陷入麻烦。
在原来的主类上面插入一个新的cpp类。目前,它基本上会将所有调用重定向到当前的主类,但目标是使这个新类的API尽可能清晰和简洁。
一旦完成了这些,就可以在新类中添加新功能。
至于现有的功能,当它们变得足够稳定时,您必须逐步将它们移动到新的类中。对于这段代码,您将失去SCC帮助,但是对此没有太多办法。只要选择合适的时机。
我知道这并不完美,但我希望它能有所帮助,这个过程必须适应您的需要!
额外的信息
注意,Git是一个SCC,它可以从一个文件跟踪代码片段到另一个文件。我听说过关于它的好东西,所以它可以帮助你逐步转移你的工作。
Git是围绕blob的概念构建的,如果我理解正确的话,blob表示代码文件的片段。在不同的文件中移动这些片段,Git会找到它们,即使您修改了它们。除了下面评论中提到的Linus Torvalds的视频之外,我还没有找到关于这个问题的一些清楚的东西。