循环引用是否必要?

时间:2010-08-27 15:54:57

标签: visual-studio circular-dependency circular-reference

我继承了一个Visual Studio解决方案,它包含了Projects之间的大量循环引用。

是否存在可以远程接受的情况?

试图证实我怀疑这个应用程序的设计非常糟糕。提前谢谢。

4 个答案:

答案 0 :(得分:8)

我曾读过一篇专栏文章,比较了3种模型:Spaghetti模型,Lasagna模型和Ravioli模型。

Spaghetti 模型中,所有代码都相互关联,没有明确的结构。这太可怕了,我们大家都同意这一点。

Lasagna 模型中,代码分为不同的图层,只有更高级别的图层可以访问较低级别的图层,而不是相反。

Ravioli 模型中,代码分组为较小的模块。每个模块只公开需要公开的内容,但每个模块仍然可以访问每个其他模块。

大约10年前,在我看来,Ravioli模型比Lasagna模型更好。毕竟,在Java中,您还拥有可以轻松相互调用的Java模块(我的印象是所有不同的Java模块之间没有真正的结构)。对我来说,Lasagna模型似乎是非面向对象旧代码的结果,而Ravioli模型似乎更现代,更面向对象。

如今,我倾向于回到Lasagna模型,但内置了Ravioli模型。这是:

  • 应用程序使用不同的层构建,例如Lasagna模型
  • 但是在这些层中,代码仍然可以在不同的模块之间分开,这些模块可以相互访问,就像在Ravioli模型中一样。

某些循环引用可能很难或无法删除。一个例子如下: 假设您的应用程序和Debug类中有一个FileWriter类。 Debug类需要FileWriter类,因为它需要编写带有调试信息的文件。 另一方面,FileWriter类也可能希望使用Debug类。

请注意,此示例中的循环引用可能已经导致问题(FileWriter类可以在编写行时调用Debug类,但Debug类使用FileWriter类来编写调试信息,结果:堆栈溢出)。

在这种情况下,可以通过不在Debug类中使用FileWriter类来轻松解决问题,而是使用本机iostream(如果您在C ++中开发)。在其他情况下,问题可能更难解决。

答案 1 :(得分:2)

良好的软件是分层设计的,它们之间有明确划分的界限。即:如果你有一个图层,你需要能够清楚地表达它的作用,它为什么存在,以及它依赖什么。圆形使得这很难实现,通常应该删除。 (Microsoft已经花了很多精力在Windows 7中通过删除循环来改进Windows的分层。)

  

试图证实我怀疑这个应用程序的设计非常糟糕。

这肯定会支持这一理论,但IMO,你需要的不仅仅是一些循环引用来得出这个结论。

回答您的原始问题:是的,循环引用有时会有所帮助。相互递归函数就是这种事物的一个很好的例子。但是......这是一个安全隐藏在模块中的循环引用。对于模块间依赖性,循环依赖通常意味着您的代码未在模块中正确分割,这可能需要一些重要的重构来修复。 (包括添加新的抽象来弥补差距等)。

答案 2 :(得分:2)

具体而言,我建议使用NDepend来检测并避免dependency cycles

alt text

摘自文章(我写道):Control component dependencies to gain clean architecture

组件之间的依赖性循环导致通常称为意大利面条代码或纠结代码。如果组分A取决于取决于取决于A的C的B,则组分A不能独立于B和C进行开发和测试.A,B和C形成不可分割的单元,一种超级组分。由于规模现象的不经济性,这种超级组件的成本高于A,B和C的成本之和(在软件估算中有详细记载:Steve McConnell揭开黑暗艺术的神秘面纱)。基本上,这表明开发不可分割的代码片段的成本呈指数级增长。

这表明开发和维护1,000 LOC(代码行)可能比开发和维护500 LOC的成本高出三到四倍,除非它可以分成两个独立的500个LOC块。因此,与意大利面条的比较描述了无法维持的纠结代码。为了使架构合理化,必须确保组件之间没有依赖循环,还要检查每个组件的大小是否可接受(500到1000 LOC)。

答案 3 :(得分:1)

循环项目引用是设计不良的标志,如果可能的话应该删除。

我可以想到保留循环引用的唯一理由是回溯问题。即使这样,似乎可以通过使用类型转发器和另一个程序集

来修复它