所以我们在项目中有这个巨大的mainmodule.cpp源文件(11000行很大吗?),每次我不得不触摸它时,我都会畏缩。
由于这个文件是如此的核心和大,它不断积累越来越多的代码,我想不出一个好方法来让它实际上开始缩小。
该文件在我们产品的几个(> 10)维护版本中被使用和积极更改,因此很难重构它。如果我“简单地”将其拆分为3个文件,那么从维护版本合并回更改将成为一场噩梦。而且,如果您拆分具有如此长而丰富历史的文件,跟踪和检查SCC历史中的旧更改突然变得非常困难。
这个文件基本上包含了我们程序的“主类”(主要的内部工作调度和协调),所以每次添加一个特性,它也会影响这个文件,每次它的增长。:-(
在这种情况下你会怎么做?关于如何在不打乱SCC工作流程的情况下将新特性移动到单独的源文件中,您有什么想法吗?
(注意:我们使用c++和Visual Studio;我们使用AccuRev作为SCC,但我认为SCC的类型在这里并不重要;我们使用Araxis Merge来做实际的文件比较和合并)
我认为最好创建一组映射到mainmodule.cpp的API点的命令类。
一旦它们就位,您将需要重构现有的代码库,以通过命令类访问这些API点,一旦完成,您就可以自由地将每个命令的实现重构为新的类结构。
当然,对于一个11 KLOC的类,其中的代码可能是高度耦合和脆弱的,但是创建单独的命令类比任何其他代理/外观策略都更有帮助。
我并不羡慕这项任务,但随着时间的推移,如果不加以解决,这个问题只会变得更糟。
更新
我建议Command模式比Facade更可取。
在一个(相对)单一的Facade上维护/组织许多不同的命令类是可取的。将一个Facade映射到一个11 KLOC文件本身可能需要分解成几个不同的组。
为什么要费心去弄清楚这些门面组呢?使用命令模式,你将能够对这些小类进行有机分组和组织,因此你有更多的灵活性。
当然,这两种选择都比单一的11 KLOC和不断增长的文件要好。
我的同情-在我以前的工作中,我遇到过类似的情况,一个文件比你必须处理的文件大几倍。解决方案是:
编写代码详尽地测试程序中的函数。听起来你还没有掌握这个…
确定一些可以抽象为帮助器/实用程序类的代码。不需要很大,只是一些不是你的“主要”类的真正一部分。
重构2中确定的代码。进入一个单独的班级。
重新运行测试,以确保没有损坏。
当你有时间的时候,去2。并根据需要重复以使代码易于管理。
在第3步中构建的类。迭代可能会增加以吸收更多适合于新清除的函数的代码。
我还可以补充:
0:购买Michael Feathers关于使用遗留代码的书
不幸的是,这种类型的工作太常见了,但我的经验是,在保持工作的同时,能够使工作但可怕的代码逐渐变得不那么可怕是有很大价值的。
一个重要的建议:不要将重构和错误修复混合在一起。您需要的是程序的版本与以前的版本相同,只是源代码不同。
一种方法是开始将最小的函数/部分分割到它自己的文件中,然后使用头文件include(从而将main.cpp转换为#includes列表,这本身听起来有点代码味道*尽管我不是c++专家),但至少现在它被分割为文件)。
然后,您可以尝试将所有维护版本切换到“新的”main.cpp或任何您的结构。再次重申:没有其他更改或错误修复,因为跟踪这些是令人困惑的地狱。
另一件事:尽管您可能希望一次性完成整个重构,但您可能会贪多嚼不烂。也许只是选择一两个“部分”,把它们放到所有的版本中,然后为你的客户增加一些更多的价值(毕竟,重构并不会直接增加价值,所以它是一种成本,必须被证明是合理的),然后再选择另外一两个部分。
显然,这需要团队中的一些纪律来实际使用拆分文件,而不是一直向main.cpp中添加新内容,但是,尝试进行一次大规模的重构可能不是最佳的行动方案。
这让我想起了我以前的工作。似乎,在我加入之前,所有东西都在一个巨大的文件中(也是c++)。然后他们将其拆分(在完全随机的点上使用include)为大约三个(仍然是巨大的文件)。正如你所预料的那样,这个软件的质量非常糟糕。该项目总标线约为40k。(几乎没有注释,但有大量重复代码)
最后,我完全重写了这个项目。我从头开始重做项目中最糟糕的部分。当然,我想到了这个新部分和其他部分之间可能的(小)接口。然后我把这个部分插入到旧的项目中。我没有重构旧代码来创建必要的接口,只是替换了它。然后我从那里迈出了一小步,重写了旧代码。
我不得不说,这花了大约半年的时间,在此期间,除了修复错误之外,没有开发旧的代码库。
编辑:
它的大小保持在40k LOC左右,但与8年前的软件相比,新应用程序在初始版本中包含了更多的功能,可能bug也更少。重写的一个原因是我们需要新的特性,而在旧代码中引入这些特性几乎是不可能的。
该软件是为一个嵌入式系统,一个标签打印机。
我应该补充的另一点是,理论上这个项目是c++的。但它根本不是面向对象的,它可能是c。新版本是面向对象的。