我继承了一个 Visual Studio 解决方案,其中包含项目之间的大量循环引用。
有没有一种情况可以远程接受?
只是想证实我怀疑这个应用程序设计得很糟糕。提前致谢。
我继承了一个 Visual Studio 解决方案,其中包含项目之间的大量循环引用。
有没有一种情况可以远程接受?
只是想证实我怀疑这个应用程序设计得很糟糕。提前致谢。
我曾经读过一个专栏,他们比较了三种模型:意大利面条模型、千层面模型和馄饨模型。
在Spaghetti模型中,所有的代码都是相互联系的,没有清晰的结构。这太可怕了,我们可能都同意这一点。
在Lasagna模型中,代码被划分为不同的层,只有高层才能访问低层,从不反过来。
在馄饨模型中,代码被分组在更小的模块中。每个模块只公开需要公开的内容,但每个模块仍然可以访问其他所有模块。
大约 10 年前,在我看来,馄饨模型比千层面模型更好。毕竟,在 Java 中,您也有可以轻松相互调用的 Java 模块(我的印象是所有不同的 Java 模块之间没有真正的结构)。对我来说,Lasagna 模型似乎是非面向对象的旧代码的结果,而 Ravioli 模型似乎更现代,更面向对象。
如今,我倾向于回到千层面模型,但内置了馄饨模型。这是:
某些循环引用可能很难或不可能删除。下面是一个示例:假设您的应用程序中有一个 FileWriter 类和一个 Debug 类。Debug 类将需要 FileWriter 类,因为它需要编写带有调试信息的文件。另一方面,FileWriter 类可能还想使用 Debug 类。
注意这个例子中的循环引用可能已经导致问题(FileWriter类可以在写一行的时候调用Debug类,但是Debug类使用FileWriter类来写调试信息,结果:栈溢出)。
在这种情况下,通过不使用 Debug 类中的 FileWriter 类,而是使用本机 iostream(如果您使用 C++ 进行开发),可以轻松解决问题。在其他情况下,问题可能更难解决。
好的软件是分层设计的,它们之间有明确的界限。即:如果你有一个层,你需要能够清楚地阐明它的作用,它为什么存在,以及它依赖于什么。循环使这难以实现,通常应该被删除。(微软在 Windows 7 中花费了很多精力来改善 Windows 的分层,通过删除循环。)
只是想证实我怀疑这个应用程序设计得很糟糕。
这肯定会支持该理论,但是 IMO,您需要的不仅仅是几个循环引用来得出这个结论。
回答您最初的问题:是的,循环引用有时会有所帮助。相互递归函数就是这类事情的一个很好的例子。但是......这是一个安全地隐藏在模块中的循环引用。对于模块间的依赖关系,循环依赖通常意味着您的代码没有正确地跨模块拆分,这可能需要进行一些重大的重构来修复。(包括添加新类型的抽象来弥补差距等)
具体来说,我建议使用 NDepend 来检测和避免依赖循环。
文章摘录(我写的):控制组件依赖以获得干净的架构
组件之间的依赖循环导致了通常所说的意大利面条代码或纠结代码。如果组件 A 依赖 B 依赖 C 又依赖 A,则组件 A 不能独立于 B 和 C 进行开发和测试。 A、B 和 C 形成一个不可分割的单元,一种超级组件。由于规模不经济现象,这个超级组件的成本比 A、B 和 C 的成本总和要高(在软件估算:史蒂夫·麦康奈尔的《揭秘黑色艺术》中有详细记载)。基本上,这认为开发一段不可分割的代码的成本呈指数级增长。
这表明开发和维护 1,000 LOC(代码行)的成本可能是开发和维护 500 LOC 的三到四倍,除非它可以分成两个独立的块,每块 500 LOC。因此,与描述无法维护的纠结代码的意大利面条进行比较。为了使架构合理化,必须确保组件之间没有依赖循环,还要检查每个组件的大小是否可接受(500 到 1000 LOC)。
循环项目引用是糟糕设计的标志,应尽可能删除。
我能想到的保留循环引用的唯一理由是向后兼容问题。即使那样,它似乎也可以通过使用类型转发器和另一个程序集来修复