所以我们在项目中有这个巨大的mainmodule.cpp源文件(11000行很大吗?),每次我不得不触摸它时,我都会畏缩。
由于这个文件是如此的核心和大,它不断积累越来越多的代码,我想不出一个好方法来让它实际上开始缩小。
该文件在我们产品的几个(> 10)维护版本中被使用和积极更改,因此很难重构它。如果我“简单地”将其拆分为3个文件,那么从维护版本合并回更改将成为一场噩梦。而且,如果您拆分具有如此长而丰富历史的文件,跟踪和检查SCC历史中的旧更改突然变得非常困难。
这个文件基本上包含了我们程序的“主类”(主要的内部工作调度和协调),所以每次添加一个特性,它也会影响这个文件,每次它的增长。:-(
在这种情况下你会怎么做?关于如何在不打乱SCC工作流程的情况下将新特性移动到单独的源文件中,您有什么想法吗?
(注意:我们使用c++和Visual Studio;我们使用AccuRev作为SCC,但我认为SCC的类型在这里并不重要;我们使用Araxis Merge来做实际的文件比较和合并)
我的0.05欧分:
重新设计整个混乱的系统,考虑到技术和业务需求,将其拆分为子系统(=许多并行维护轨道,每个并行维护轨道的代码库可能不同,显然需要高可修改性等等)。
在划分子系统时,分析变化最大的地方,并将其与不变的部分分开。这应该会显示出问题所在。将最易变化的部分分离到它们自己的模块中(例如dll),这样模块API就可以保持完整,而不需要一直破坏BC。这样,如果需要,您可以为不同的维护分支部署不同版本的模块,同时保持核心不变。
重新设计很可能需要一个单独的项目,试图做一个移动的目标是行不通的。
至于源代码历史,我的意见是:为了新代码忘掉它吧。但是请将历史记录保存在某个地方,以便在需要时进行检查。我打赌你开始之后就不那么需要它了。
对于这个项目,您很可能需要得到管理层的支持。你可以用更快的开发时间、更少的bug、更容易的维护和更少的混乱来反驳。类似于“积极地使我们的关键软件资产具有未来的可靠性和维护可行性”:)
至少这是我开始解决问题的方式。
所以从一开始重写产品代码的API是一个坏主意。需要做两件事。
首先,您需要让您的团队决定对该文件的当前生产版本进行代码冻结。
第二,您需要使用这个生产版本并创建一个分支,该分支使用预处理指令来管理构建,以分割大文件。使用JUST预处理器指令(#ifdefs, #includes, #endifs)拆分编译比重新编码API更容易。对于您的sla和持续的支持来说,这绝对更容易。
在这里,您可以简单地删除类中与特定子系统相关的函数,并将它们放在一个文件(例如mainloop_foostuff.cpp)中,并将其包含在mainloop.cpp中的正确位置。
OR
一种更耗时但健壮的方法是设计一个内部依赖关系结构,在包含内容的方式上具有双重间接性。这将允许您分割内容,并仍然照顾到共同依赖关系。注意,这种方法需要位置编码,因此应该加上适当的注释。
这种方法将包括基于您正在编译的变体而使用的组件。
基本结构是mainclass.cpp将在如下语句块后包含一个名为MainClassComponents.cpp的新文件:
#if VARIANT == 1
# define Uses_Component_1
# define Uses_Component_2
#elif VARIANT == 2
# define Uses_Component_1
# define Uses_Component_3
# define Uses_Component_6
...
#endif
#include "MainClassComponents.cpp"
MainClassComponents.cpp文件的主要结构将在那里计算子组件中的依赖关系,如下所示:
#ifndef _MainClassComponents_cpp
#define _MainClassComponents_cpp
/* dependencies declarations */
#if defined(Activate_Component_1)
#define _REQUIRES_COMPONENT_1
#define _REQUIRES_COMPONENT_3 /* you also need component 3 for component 1 */
#endif
#if defined(Activate_Component_2)
#define _REQUIRES_COMPONENT_2
#define _REQUIRES_COMPONENT_15 /* you also need component 15 for this component */
#endif
/* later on in the header */
#ifdef _REQUIRES_COMPONENT_1
#include "component_1.cpp"
#endif
#ifdef _REQUIRES_COMPONENT_2
#include "component_2.cpp"
#endif
#ifdef _REQUIRES_COMPONENT_3
#include "component_3.cpp"
#endif
#endif /* _MainClassComponents_h */
现在,为每个组件创建一个component_xx.cpp文件。
当然,我使用数字,但你应该使用一些更符合逻辑的基于你的代码。
使用预处理器可以让你把事情分开,而不必担心API的变化,这在生产中是一个噩梦。
一旦你确定了产品,你就可以开始重新设计了。