所以我们在项目中有这个巨大的mainmodule.cpp源文件(11000行很大吗?),每次我不得不触摸它时,我都会畏缩。
由于这个文件是如此的核心和大,它不断积累越来越多的代码,我想不出一个好方法来让它实际上开始缩小。
该文件在我们产品的几个(> 10)维护版本中被使用和积极更改,因此很难重构它。如果我“简单地”将其拆分为3个文件,那么从维护版本合并回更改将成为一场噩梦。而且,如果您拆分具有如此长而丰富历史的文件,跟踪和检查SCC历史中的旧更改突然变得非常困难。
这个文件基本上包含了我们程序的“主类”(主要的内部工作调度和协调),所以每次添加一个特性,它也会影响这个文件,每次它的增长。:-(
在这种情况下你会怎么做?关于如何在不打乱SCC工作流程的情况下将新特性移动到单独的源文件中,您有什么想法吗?
(注意:我们使用c++和Visual Studio;我们使用AccuRev作为SCC,但我认为SCC的类型在这里并不重要;我们使用Araxis Merge来做实际的文件比较和合并)
在我看来,您现在面临的是一堆代码异味。首先,主类似乎违反了开/闭原则。这听起来也像是承担了太多责任。因此,我认为代码比实际需要的更加脆弱。
虽然我可以理解您对重构后可跟踪性的担忧,但我认为该类相当难以维护和增强,而且您所做的任何更改都可能导致副作用。我假设这些的成本超过了重构类的成本。
在任何情况下,由于代码气味只会随着时间的推移而变得更糟,至少在某些时候,这些成本将超过重构的成本。根据你的描述,我认为你已经过了临界点。
重构应该分小步骤进行。如果可能的话,在重构任何东西之前添加自动测试来验证当前行为。然后挑选出独立功能的小区域,并将其提取为类型,以便委派职责。
无论如何,这听起来像是一个大项目,所以祝你好运:)
下面是我所想到的解决这些问题的唯一办法。所述方法的实际增益是演化的累进性。这里没有革命,否则你很快就会陷入麻烦。
在原来的主类上面插入一个新的cpp类。目前,它基本上会将所有调用重定向到当前的主类,但目标是使这个新类的API尽可能清晰和简洁。
一旦完成了这些,就可以在新类中添加新功能。
至于现有的功能,当它们变得足够稳定时,您必须逐步将它们移动到新的类中。对于这段代码,您将失去SCC帮助,但是对此没有太多办法。只要选择合适的时机。
我知道这并不完美,但我希望它能有所帮助,这个过程必须适应您的需要!
额外的信息
注意,Git是一个SCC,它可以从一个文件跟踪代码片段到另一个文件。我听说过关于它的好东西,所以它可以帮助你逐步转移你的工作。
Git是围绕blob的概念构建的,如果我理解正确的话,blob表示代码文件的片段。在不同的文件中移动这些片段,Git会找到它们,即使您修改了它们。除了下面评论中提到的Linus Torvalds的视频之外,我还没有找到关于这个问题的一些清楚的东西。
你担心文件的大小。
从历史上看,C程序的文件大小是由机器PDP11/40的限制决定的。
我使用的这个可以处理最大4096字节的文件。为了解决这个问题
C编译器使用#include并发明了.h文件来帮助链接器和分段加载器,因为
加载器必须动态交换(因此在Intel架构中使用段寄存器)。
Small files solved the problem but left an historical legacy. Programmers now believe that small files
are the only way to program. You have a machine with 4 gigabytes (vs 8 kilobytes on the 11/40).
You have a machine with 3 billion instructions per second (vs 500 kilo instructions on the 11/40).
You have a compiler that can block optimize code it can see (as opposed to linking .o files which
it cannot see). You have a machine that is bandwidth limited by disk I/O but you want to create
500 tiny .c, .h, and .o files, possibly multiple times with the .h includes.
大的C文件绝对没有错。编译器可以大量优化
磁盘I/O最小,链接器时间消失,编辑器可以找到琐碎的东西
一个花哨的IDE,……
11000行对于今天来说是一个微不足道的文件。把自己从历史中解放出来。
我认为最好创建一组映射到mainmodule.cpp的API点的命令类。
一旦它们就位,您将需要重构现有的代码库,以通过命令类访问这些API点,一旦完成,您就可以自由地将每个命令的实现重构为新的类结构。
当然,对于一个11 KLOC的类,其中的代码可能是高度耦合和脆弱的,但是创建单独的命令类比任何其他代理/外观策略都更有帮助。
我并不羡慕这项任务,但随着时间的推移,如果不加以解决,这个问题只会变得更糟。
更新
我建议Command模式比Facade更可取。
在一个(相对)单一的Facade上维护/组织许多不同的命令类是可取的。将一个Facade映射到一个11 KLOC文件本身可能需要分解成几个不同的组。
为什么要费心去弄清楚这些门面组呢?使用命令模式,你将能够对这些小类进行有机分组和组织,因此你有更多的灵活性。
当然,这两种选择都比单一的11 KLOC和不断增长的文件要好。