类 之间的循环依赖:它们为什么不好以及如何摆脱它们?

Circular dependencies between classes: why they are bad and how to get rid of them?

我觉得循环依赖(又名循环引用)意味着糟糕的设计并且会损害项目。我怎样才能说服我的队友和我的经理?

我的项目是一团依赖。有没有一种方法可以摆脱错误的依赖,然后保持清晰?

为什么循环依赖 (CiD) 不好?

两个原因:

  1. 可维护性。

    您希望您的代码是分层的,即您想要一个自上而下的依赖关系图(该图显示所有箭头向下,没有箭头向上)。 如果您有 CiD,则您的代码不是分层的。

    为什么分层代码意味着可维护性?因为,每次你改变一个界面 class,你可以确定它下面的任何东西都不会受到影响。这些知识使维护和开发 系统更便宜,更不容易出错。

    好消息是,有了像 Visual Studio 这样的现代开发工具, Eclipse, NetBeans, 和 IntelliJ,还算可以 轻松为您的项目生成图表。并且,如果还没有工具,则有一个simple trick

  2. 可靠性。

    您不希望在生产中出现意外的无限递归。例如,当您的配置想要 记录错误并且您的错误记录器想要从配置中读取日志文件的名称(您的测试将 pass,因为测试环境没有报错)。 (不幸的是,意外的递归可能是 即使没有 CiD 也能开发。)

有好的CiD吗?

一些 CiD 是有效的、有用的,并且不影响可维护性或可靠性:字符串和对象、文件和文件夹、节点和边缘。通常,这样的圈子位于一个包内,不会导致包之间的循环依赖。

如何检测包裹 CiD?

您可以在依赖关系图中直观地检测 CiD(请参阅上面生成工具的链接)。

如果您的项目很大,或者您想持续监视 CiD,那么实现一个工具很简单,它使用 反射来检测 CiD(遍历 classes 或包深度优先并在第一个反向引用处停止)。

请注意,如果一个包在另一个包中声明,这并不意味着它们相互依赖,除非它们中的 classes 相互引用。

我的项目是一团乱麻。有修复方法吗?

就在这里。以下是步骤:

  1. 设计所需的结构。

    一个。创建现有包的简单列表,如下所示:

    如果您的包结构是分层的,请将其展平,并且不要忘记根包。

    b。整理包裹。
    重新排序列表,使具有较低抽象级别的包更接近底部,而具有 更高的抽象级别更接近顶部。 如果一个包包含 class 低抽象级别和高抽象级别,您可能希望将此包分成两部分。

    如果您的包裹太多,请先将它们分成几层,对这些层进行排序,然后对这些层内的包裹进行排序。 这一步应该会产生你想要的包顺序,你希望依赖关系下降而不是上升。

    c。以相同的方式在包中组织 classes。

  2. 使过程可衡量。

    测量到所需结构的距离,以便在接近目标时看到进度。 对于每个 class,计算错误依赖项的数量(依赖项向上)。这些数字的总和将 成为你的指标。最后,它将为零。
    设置监控以检测新的错误依赖项。你想在你的队友(和你自己)之前阻止他们 即将检查另一个错误的依赖项。

  3. 错误的依赖一一解决。通常从底部到顶部更容易,因为你会想要澄清 首先是基础知识。

    这些提示可能会有所帮助:

    A. 你有一个“死星”或“上帝对象”,即一个被 classes 知道的对象。在 换句话说,这样的 class 是许多循环依赖的一部分,这可能导致每个循环的依赖 class 几乎每隔 class.

    解决方案:
    大多数死星可以通过将它们分成两个(或更多)class 来解决,其中一个 class 仅包含 状态和非常基本的操作,另一个 class 包含高级操作(通常第二个 class 是静态的)。

    B. 您在 class 之间没有圆圈,只是在包之间。

    解决方案:
    考虑将一些 classes 移动到其他软件包。

    C. 你有一个 class 使用上层 class 的一些方法,但不拥有它的实例化。

    解决方案:
    使用回调接口或回调方法(模式观察者)。

    D. 你有两个 classes 相互创建并使用彼此的方法。

    解决方案:

    • 将class合并为一个class。
    • 将 classes 放入一个包中,并将它们的关系声明为良好的 CiD。
    • 为 class 之一创建工厂 class 和接口。

如何保持项目的清晰度?

如果你有一个小团队和一个小项目,简单地向你的队友解释规则,偶尔检查一下图表。

如果项目又大又复杂,你应该建立一个流程,每次有人来的时候都会收到提醒或拒绝 编译或检查错误的依赖项。

首先,当您的项目非常简单时,反模式设计不会造成问题,它确实会让事情变得更容易。它只会在项目变得复杂时出现问题。选择点来平衡容易和正确是设计的一部分。说服别人总是需要一个真正的错误。

简而言之,为了处理循环依赖,你总是需要将一个与多任务分开。例如:enter image description here

原则是让每个单位专注于它的核心业务或服务。如果你有一个圆圈,有时意味着你制作了一个具有多核心业务的单元。但实际上总是有矿工周期,因为真正的业务流程是这样的。与单元核心无关的可以。

顺便说一句,有人可以帮我内嵌图片吗?谢谢