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

pol*_*a-c 3 dependencies design-patterns circular-dependency dependency-management

我觉得循环依赖意味着糟糕的设计并损害项目.我怎样才能说服我的队友和我的经理?

我的项目是依赖性混乱.有没有一种方法可以摆脱错误的依赖关系,然后保持清晰度?

pol*_*a-c 7

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

两个原因:

  1. 可维护性.

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

    为什么分层代码意味着可维护性?因为,每次更改类的界面时,都可以确保它下面的任何内容都不会受到影响.这些知识使系统的维护和开发更便宜且不易出错.

    好消息是,使用Visual Studio,Eclipse, NetBeansIntelliJ等现代开发工具, 为项目生成图表相当容易.

  2. 可靠性.

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

有没有好的CiD?

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

如何检测包CiD?

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

如果您的项目很大或者您想要连续观察CiD,那么实现一个使用反射来检测CiD的工具很简单(遍历类或包深度优先并在第一个返回参考处停止).

注意,如果一个包在其他包中声明,这并不意味着它们相互依赖,除非它们的类相互引用.

我的项目是依赖性混乱.有没有一种方法来解决它?

依赖性混乱 就在这里.以下是步骤:

  1. 设计所需的结构.

    一个.创建现有包的简单列表,如下所示: 在此输入图像描述

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

    湾 组织包.
    重新排序列表,使具有较低抽象级别的包更接近底部,具有较高抽象级别的包更接近顶部.如果包中包含低和高抽象级别的类,您可能希望将此包破解为两个.

    如果你有太多的包,首先将它们分成几层,订购层,然后在这些层内订购包.此步骤应该会产生所需的包顺序,您希望依赖关系在哪里停止而不是向上.

    C.以相同的方式组织包中的类.

  2. 使流程可衡量.

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

  3. 逐个解决错误的依赖关系.通常,从下到上更容易,因为您首先要澄清基础知识.

    这些提示可能有所帮助

    A.你有一个"死亡之星"或"上帝对象",即一个它所知道的类所知的对象.换句话说,这样一个类是许多循环依赖的一部分,可能导致每个类的依赖几乎所有其他课程.

    解决方案:
    大多数死亡星可以通过将它们分成两个(或更多)类来解决,其中一个类只包含状态和非常基本的操作,另一个类包含高级操作(通常第二个类是静态的). 在此输入图像描述

    B.类之间没有一个圆圈,只是在包之间.

    解决方案:
    考虑将某些类移动到其他包.

    C. 你有一个使用某些上层类方法的类,但它没有自己的实例化.

    解决方案:
    使用回调接口或回调方法(模式Observer). 在此输入图像描述

    D. 你有两个相互创造并使用彼此方法的类.

    解决方案:

    • 将类组合成一个类.
    • 将类放入一个包中,并将它们的关系声明为一个好的CiD.
    • 为其中一个类创建Factory类和接口.

在此输入图像描述

如何在项目中保持清晰度?

如果你有一个小团队和一个小项目,只需向你的队友解释规则,偶尔检查图表.

如果项目很大且很复杂,那么每当有人编译或检查错误的依赖项时,您应该建立一个接收警报或拒绝的过程.