【问题标题】:When exactly does a class/package depend on another?一个类/包究竟什么时候依赖于另一个?
【发布时间】:2011-11-09 19:03:04
【问题描述】:

许多文章/书籍/.. 谈论类或包依赖,很少解释它是什么。我确实找到了一些定义,但它们各不相同,可能并不涵盖所有情况。例如:

要考虑的其他方面是方法参数、依赖注入、面向方面的编程、泛型。还有其他方面吗?

那么,你能否给出一个(正式的)类之间和包之间的依赖关系的定义,它是万无一失的,并且涵盖了所有这些情况和方面?

【问题讨论】:

  • 你想解决什么问题?
  • @bancer:不是一个特别的问题,只是总体上改进我的(干净代码)开发,例如通过遵循 Bob 叔叔的建议,了解所涉及的指标(例如 jdepend 中给出的),以及各种依赖注入。

标签: oop architecture dependencies


【解决方案1】:

如果您在控制反转或依赖注入的上下文中要求依赖,那么您可能对直接相互交互的类感兴趣。这意味着主要是构造函数参数和属性。

在 UML 域图的上下文中,您可能对“真实世界”的依赖关系感兴趣。狗需要食物。那是一种依赖。狗的Bark() 方法返回一个Sound 对象:在UML 域模型中,这不是您感兴趣的东西。狗不依赖声音的存在。

您也可以从哲学角度出发:所有类都相互依赖以实现共同目标;一个(希望)很棒的软件。

所以,总而言之,依赖或耦合不是是或否的问题。这实际上取决于上下文和耦合度(弱,强)。我认为这解释了为什么存在许多不同的依赖定义。

【讨论】:

  • 好点,我也认为依赖是一个程度,而不是是/否的问题(又名二进制)。但是在依赖倒置/静态分析/设计原则a la SOLID的上下文中,每个人都在二进制意义上使用它,这引起了我的困惑和这个问题。
  • 我认为依赖是二元的——这只是何时执行的问题。依赖倒置只是允许您将该检查推迟到运行时。
  • @Adrian K:我不同意。我可以轻松地编写具有“可选”依赖项的软件。在这种情况下,从技术上讲,没有依赖关系,但是用户可能会对此有不同的看法,因为他或她可能需要它来支持某个功能,从而创建功能依赖关系。
  • @Jonathan:我认为 Bryan 的意思是依赖是模糊/渐进/连续的。在您的示例中,您似乎具有可选但仍然是二元依赖(好的,可选依赖可以被视为一个附加的中间/三元值)。
【解决方案2】:

我不久前就该主题写了一篇博文:Understanding Code: Static vs Dynamic Dependencies。基本上,您需要区分 static 依赖项,即编译器在编译时解析的那些,以及 dynamic 依赖项,那些由运行时(JVM或 CLR) 在运行时。

静态依赖通常是由调用静态/最终方法、读/写字段、在类 C 的定义中、由 C 实现接口 I 引起的……所有这些可以在字节码和源代码中明确找到的代码元素之间的关联。

动态依赖通常由在编译时抽象方法调用的所有事物引发,例如对抽象/虚拟方法的调用(多态性)、使用接口类型化的变量或参数(实现类是抽象的)在编译时),还有委托(.NET)或指向函数的指针(C++)。

大多数时候,当您在文献中阅读有关依赖项时,他们都在谈论静态依赖项

静态依赖项是直接的(意味着不传递)。我在博文中提到的像NDepend 这样的工具也可以从直接静态依赖项 的集合中推断出间接(或称其为传递)静态依赖项。 p>

我在博文中捍卫的观点是,在理解和维护程序时,需要主要关注静态依赖项,即源代码中的静态依赖项。。事实上,抽象设施被用来,嗯......抽象,调用者的实现。这使得源代码更容易开发和维护。然而,在某些情况下,通常是在调试时,需要知道运行时抽象背后的真正含义。

【讨论】:

  • 这太有趣了:当你写这篇文章时,我只是在听你的 hanselminute-interview。我特别喜欢你的评论,即非线性指标太间接,因此太模糊而无用(我理解正确吗?)。您是否知道任何 Java 工具,我可以在其中查询指标,如您的 NDepend(包括增量)?我觉得太棒了!非常感谢你的博文,我一定会读的:)
  • 我阅读了您的博文,它给了我一些见解,非常感谢 :) 不过,我确实还有一些问题:我作为对 LeleDumbo 帖子的回复发布的问题,以及我的示例是否在静态依赖图不是动态依赖图的子图,在stackoverflow.com/questions/7070570/… 给出,是真的。
  • 是的,你理解正确 :) 很高兴这篇博文对你有用。对于 Java,试试 XDepend.com,也就是 Java 的 NDepend。
  • XDepend 看起来真的很棒,可惜它需要 Windows 和 .Net Framework :(
  • 我刚刚将 LeleDumbo 的答案标记为解决方案 - 但您的答案同样有帮助。非常感谢!
【解决方案3】:

这篇文章是关于静态依赖的——关于动态依赖和区别,请参阅 Patrick Smacchia 的回答。

以一种易于理解的方式:当没有 B 就不能独立使用 A 时,实体(类或包)A 依赖于实体 B。

继承、聚合、组合,都引入了相关实体之间的依赖关系。

所以不存在对接口的依赖?

有,但界面只是胶水。

继承呢?

见上文。

所以依赖不仅是包的传递关系,而且是类级别的传递关系?

是的。

但是你如何定义“依赖”?

见上文“易于理解”的定义。也与您发布的第三个定义有关。


更新:

所以如果你在包P1中有接口A,包P2中的类C使用A作为

  • 方法参数,或
  • 局部变量通过 AOP 编织到 C 中,或者
  • class C implements A,或
  • class C<E extends A>,

那么 C 依赖于 A 而 P2 依赖于 P1。

但是如果接口 A 是由 B 类和 C 类程序针对接口 A 实现的,并且仅通过依赖注入使用 B,那么 C 仍然(静态!)仅依赖于 A,而不依赖于 B,因为依赖注入点是它不会使粘合组件依赖。

【讨论】:

  • 如果依赖是传递的,这意味着大多数类相互依赖。如果A 依赖于B,而B 依赖于C,那么A 也依赖于C?在大多数情况下,我认为不会。
  • @Bryan:我发现类级别的传递性也很奇怪。所以也许 Martin Fowler 的定义应该是“A 类依赖于 B 类,如果对 B 的定义的更改可能会导致对 A 的直接更改,即不会通过连锁反应”。
  • 你认为依赖关系应该在包级别传递吗?
  • @Bryan IMO,是的。但在这种情况下,A 通过 B 隐式依赖于 C。为了证明,您不能使用 A 或 B 甚至两者都构建应用程序。当你需要 A 时,你同时需要 B 和 C。
  • @DaveBall 第一个问题:是的。第二个问题:不,A 不依赖于 C 也不依赖于 B。依赖注入不会使粘合组件依赖。但也许我没有正确理解你的意思,你能举个具体的例子吗?
猜你喜欢
  • 2020-10-30
  • 2010-10-23
  • 1970-01-01
  • 1970-01-01
  • 2021-04-15
  • 2014-09-09
  • 2017-12-11
  • 2023-02-25
  • 1970-01-01
相关资源
最近更新 更多