循环依赖问题

44

我有两个项目。

项目#2 引用了项目#1

现在我需要在项目#1中引用项目#2,但是vs.net会抱怨有循环依赖关系。

有什么解决办法吗?


2
至少我们都同意了答案.. ;-). 给大家+1 - David
13个回答

84
绝对不行。循环依赖是糟糕设计的标志。我的意思不是要苛刻。有一些方法可以解决这个问题。

1)您可以将常用代码重构为另一个项目,比如项目#0。

2)可以修复您的设计,这可能是正确的方法。

Uncle Bob在Packaging Principles中有一篇很好的文章,其中包括Acyclic Dependencies Principle。http://www.objectmentor.com/resources/articles/granularity.pdf阅读此文以了解循环依赖为什么不好。


2
一种常见的修复方法是依赖于抽象,如接口,而不是具体实例,并将这些抽象移到另一个程序集中。但是,请真正考虑你的项目结构和哪些内容确实属于哪里。 - kyoryu
我将原本包含“应用程序使用日志”类和“错误日志记录”类的“Logger”包拆分为两个单独的包(大多数包不需要记录应用程序使用情况!),然后将曾经循环的那个包链接回原始包。现在是一个有向无环图。这篇文章应该是所有人必读的。+1 for linked article. - DFTR
@kyoryu,你在谈论文章中提到的依赖反转原则(DIP)吗?- 我同意,你应该尝试找出为什么可能存在循环依赖的原因,并在依赖于抽象之前更改设计元素(如果可以)。 - DFTR
1
我认为,从链接文章中得出的结论并不是循环依赖关系是糟糕设计的一个指标,而是循环依赖关系本身就是糟糕的设计。循环依赖关系本身会对责任模糊、模块封装、编译和包更新等方面产生负面影响。因此,如果你能够在没有这些缺点的情况下拥有循环依赖关系,那么就不会有问题。或许这只是语义上的区别,但我认为值得详细说明。 - Alex

17

重构你的项目,将共同要素提取到一个名为“项目#0”的项目中,然后将项目#1和项目#2引用该项目。


8
将两者合并成一个或重新设计。

5

这表明你的设计存在问题。如果确实需要两个或多个类型相互感知,那么它们应该存在于同一个程序集中。


2
如果它们需要使用不同的语言呢? - Joshua
你能举个例子吗? - Adam Ralph
在 VS 2008 中,只有 VB .NET 可以使用异常过滤器,而只有 C# 可以使用 Linq 表达式。很容易想象这些会出现在冲突的设置中的情况。 - Joshua
我有一个实时示例,我犹豫是否分享,我计划使用ILMerge或类似的工具来解决它。 - Joshua
我仍然认为你可能有一个设计问题。 - Adam Ralph

5

循环依赖意味着这两个项目不再是独立的(因为只构建其中一个是不可能的)。

您需要进行重构,使其仅具有单向依赖,或者将它们合并为一个项目。


4

可以像在之前的问题中所示那样完成循环引用,但是由于大家已经阐述了原因,您不应该这样做


3

每个人都会告诉你,这是一个糟糕的设计,不要这样做等等。然而有时候说起来容易做起来难,将实现移动到单独的公共代码中并不可取。

对于这种情况,可以从一个包中发出事件并在另一个包中处理它,而不是直接调用其他包。这样,您无需使第一个组件依赖于另一个组件。

如果您仍想将实现保留在单独的包中,另一种方法是从接口派生逻辑类,并在单独的包中定义这些接口。如果您有一种实例化实现的方式,例如通过依赖注入或其他手段,那么这种方法就可以使用。


3

不要忽视项目结构,尝试使用基于抽象的排序方式——从低层到高层。


2
与之前所说的相反,有时候循环依赖是无法避免的。当然,线性设计有其优点(可维护性、可读性、调试等),但如果要放弃循环/双向性以此来放弃按照功能拆分项目(这并不能帮助您维护或理解代码),那就没有意义了。
解决方案: 必须使用一个带有接口的项目,这两个项目均引用该接口。 高层项目中的类实现了接口项目中定义的接口。 这样,您就可以以循环方式公开方法实现和类。
一些伪代码:
项目界面
interface IApple { void dropOnHead(IPerson person);}
interface IPerson { void eatApple(IApple apple);}

项目#1

using ProjectInterfaces;
class Apple : IApple{
  void dropOnHead(IPerson person) { log("bop");}
}

Project#2

using ProjectInterfaces;
class Person : IPerson{
  void dropOnHead(IApple apple) { log("crunch");}
}


2

我真的不是要装聪明,但更好的程序设计才是答案。


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