循环依赖是好还是坏?

21

我需要知道为什么我们需要避免循环依赖?

在现实世界中,如果我们考虑,循环依赖非常重要。比如一个朋友需要另一个朋友的某物,而另一个朋友也需要这个朋友的某物,所以这有点循环吧?

那么为什么循环依赖是一个不好的设计呢? 如果我们真的需要避免这种情况,那么在面向对象的世界中,最好的可能设计是什么?


2
根据我的经验,循环引用的主要问题在于内存管理 - 谁拥有什么? - Neil Kirk
它既不好也不坏:它就是它。如果你觉得必须这样做,请注意内存泄漏。 - YvesLeBorg
上次我检查(也就是我必须编译它)时,llvm 项目完全支持循环依赖,并且他们正在编写虚拟机/编译器。你可以以此为例,并询问为什么他们采用这种设计。 - user2485710
让我们以头文件为例:头文件 A 需要从头文件 B 中获取一些东西才能工作,而头文件 B 需要从头文件 A 中获取一些东西才能工作。但是头文件 B 不能包含头文件 A,因为头文件 A 需要先包含 B,B 又需要先包含 A,依此类推。这是在 C 和类似语言中常见的典型循环依赖问题,常见于初学者。 - Some programmer dude
1
你所举的例子:“一个朋友需要另一个朋友的东西,而另一个朋友也需要这个朋友的东西……”只有当第一个朋友不能提供第二个朋友所需的东西,除非他们提供第一个朋友所需的东西时,才能称之为“依赖关系”。如果没有依赖关系,任何一方都可以随时向另一方提供服务。因此,通常情况下,应避免循环依赖。 - Tony
2个回答

40

循环依赖的问题有点像鸡和蛋问题。

如果你依赖于我设置某些东西,而我又依赖于你设置某些东西,我们该如何开始?

这个问题的推论是,如果我引用了你的资源,你也引用了我的资源,那么我永远无法清理资源,因为这会破坏你,而你也无法清理资源,因为这会破坏我。

在两种情况下的答案都是引入一个中间人,将依赖关系从其中一方传递给他。因此,如果你将你的资源交给中间人,你就依赖于我和中间人,我依赖于中间人。这样,你可以清理资源,因为你现在不持有任何资源,我可以清理资源,因为没有人依赖于我,然后中间人可以清理资源。


3
好的通用答案,避免了处理部分复杂性的特定语言工具可能会出现的问题,并且在我所知道的所有情况下都是有用的模式。+1 - YvesLeBorg
嗯,谢了 gbjbaanb。但这也意味着在虚拟世界中,两个朋友不能直接互动,他们需要一个中间人来完成。 - Nipun
你仍然可以进行通信,但不能依赖对方。因此,一旦你有了这样的设置,你必须预料到合作伙伴可能不在场。在循环依赖中,紧密耦合是最重要的。 - gbjbaanb
@Nipun:友谊不是循环依赖。如果我需要等待我的朋友与我互动,然后才能与他们互动,而他们也需要等待我开始互动,那就是循环依赖了,但友谊并不是这样运作的。 - user2357112
谢谢......喜欢这些答案。解决了我的疑虑。 - Nipun
显示剩余2条评论

9
您需要意识到循环依赖意味着您只能一起使用相应的循环依赖类:如果您在A和B之间存在循环依赖,则无法在任何程序中独立使用A或B。回到您的问题上来:当然,您不需要其他两个朋友存在!您只需要某种方式引用它们并以可能受到其实际能力限制的方式与它们进行交互。
然而,通常可以让类对象彼此使用而不会导致循环依赖。为此,重要的是确定实际上是什么导致两个类/组件之间的依赖关系(这些不完全等价,但提供详细定义会有点冗长)。A在以下情况下依赖于B:
1. 当A包含类型为B的成员时。 2. 当A派生自类型B时。 3. 当A在函数签名的一部分中使用类型为B的值时。 4. 当A在其实现中使用B时。 5. 我可能忘记了一些原因,即类将耦合。
当两个类之间存在循环依赖时,可能有方法可以打破这种依赖关系。通常,可以通过将这两个类之一拆分为一个基类和一个派生类来打破依赖关系,其中基类不依赖于另一个类。还有许多其他方法可以打破依赖关系。John Lakos的《大规模C ++》(1996)基本上都是关于打破依赖关系并激励为什么循环依赖是不好的(我猜他会不同意这种简化的表述)。
... 是的,循环依赖是不好的:
1.它们使程序包含不必要的功能,因为会带入不需要的东西。 2.它们使软件测试变得更加困难。 3.它们使软件推理变得更加困难。 4.它们使系统的部分替换变得更加困难。 5.可能还有其他一些原因。
以上是从C ++角度出发的观点。某些循环依赖的原因可能不存在于C中,但相同的概念大致也适用于C。

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