什么算法可以分析调用依赖关系以进行库裂变?

3
假设我有一个包含许多相互依赖的函数的库,这个库太大了,我想把它分成几部分。有哪些算法可以找到适当的划分呢?
简单的例子,里面有四个函数:alpha,beta,gamma和delta。
- beta和gamma都调用了delta。 - module1调用alpha和beta。 - module2调用gamma。 - module3调用alpha,beta和gamma。
算法的输出可以是:
- LibA包含(alpha,beta) - LibB包含(gamma) - LibC包含(delta) - module1依赖于LibA - module2依赖于LibB - module3依赖于LibA和LibB - LibA依赖于LibC - LibB依赖于LibC
即它找到了最精细的Lib*划分,具有以下属性
对于所有X,如果LibX按任何方法划分为LibY和LibZ,则所有依赖于LibY的模块/库也依赖于LibZ,反之亦然。
是否有标准解决方案?

那么,在上面的例子中,如果module*也是库,我是否可以这样想:在对示例库进行分析之前,应先对这些库进行分析和拆分,以获得最佳结果? - spraff
1个回答

1

这是与C和C++程序中头文件相同的问题。

不仅仅是“调用”会创建依赖关系;任何引用,包括成员变量、静态变量甚至常量定义都会产生依赖关系。

基本上,你需要做的就是发现所有细粒度的依赖关系(这通常需要一个类似编译器的分析工具来读取代码并发现这些依赖关系,包括声明的语言元素(声明、字段、方法、类、包等,如果你是Java中心),以及其他语言元素。使用库所写语言的语义。(这种分析可能是保守的)。这本质上给你提供了一个巨大的图,其中节点是语言元素,弧是“使用”。

抽象的库打包问题是将这个图分解成块,最小化跨块依赖弧。这可能会给你带来大量的小型库。

实际问题是将一些没有实际依赖关系但通常一起使用的块分组在一起。例如,一组缓冲区访问过程可能没有任何对默认缓冲区大小定义的显式依赖关系,但您可能希望有一个包含两者的库,而不是一个只包含默认缓冲区大小声明的库。这种一起使用的概念实际上是一个问题域工件,在代码中除了一些统计共现使用之外,没有任何可见性。
这个问题的难点在于发现细粒度的语义依赖关系。您可以手动近似处理,但如果问题规模很大,您就没有兴趣去做它。(人们不会重新组织头文件的原因相同)。基本上,您需要语言工具来进行分析,大型图形管理来提出块,统计分析来获得启发式分组,并且可能需要一个UI来允许领域专家编辑分组以生成修订后的库。
然后,您需要一个工具来返回使用旧库的代码,并修改它们以使用修订后的库。库重构和代码库修订都需要大量的代码分析和更改,需要自动化。

我们的DMS软件重构工具包和其众多语言前端可能是实施这样的库重组的良好基础。我们已经考虑过为C和C++做这个,这就是我回复的原因,但即使对于我们来说,这也是一个巨大的任务。我们需要更多的动力!


Restructure101 有这个能力,可以直接将大型依赖关系图(其中依赖是指 Ira 所描述的任何引用)组织成连贯的集群。它根据启发式算法做了相当好的工作,然后您可以通过手动拖放来修订聚类,并为聚类(它们将成为您的新子库)赋予有意义的名称。如果您有非常大的库,还可以递归地执行此操作(对聚类进行聚类)。您需要自己进行代码更改,但它会输出一个列表。基于 Doxygen 的 C/C++ 版本刚刚发布。 - Chris Chedgey - Structure101
@spraff:我认为我的答案勾勒出了你所寻找的算法。相对于你的问题,它为什么不令人满意呢?(我认为你不会像快速排序那样得到一个类似代码的答案)。 - Ira Baxter
“Restructure 101”的答案看起来像是一个很棒的交互式工具,用于处理图表并手动/半自动地组织抽象概念。但我认为解决方案的难点在于提取依赖项(需要语言精确的解析器和完整的分析),生成重新构建的库,最后修改代码以使用这些库。(Restructure 101“列出”了需要进行的手动工作清单……有点像“您应该重构此代码”)。如果您的系统很大,则手动完成所有这些工作可能会非常困难,并且很难向管理层证明其合理性。 - Ira Baxter

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接