9

Many articles/books/.... talk about class or package dependency, few explain what it is. I did find some definitions, but they vary and probably don't cover all cases. E.g.:

Further aspects to consider are method parameters, dependency injection, aspect oriented programming, generics. Any more aspects?

So, can you give a (formal) definition for dependency amongst classes and amongst packages that is fool-proof and covers all these cases and aspects?

DaveFar
  • 7,078
  • 4
  • 50
  • 90
  • What problem are you trying to solve? – bancer Sep 03 '11 at 21:26
  • @bancer: not a particular problem, just improve my (clean code) development in general, e.g. by following Uncle Bob's suggestions, understanding the metrics involved (e.g. as given in jdepend), as well as the various dependency injections. – DaveFar Sep 03 '11 at 21:38

3 Answers3

3

If you are asking for dependency in the context of inversion of control or dependency injection, well, you're probably interested in classes that interact with one another directly. That means mostly constructor parameters and properties.

In the context of a UML domain diagram, you're probably interested in "real world" dependency. A dog needs food. That's a dependency. The dog's Bark() method returns a Sound object: that's not something you're interested in, in a UML domain model. The dog doesn't depend on sounds to exist.

You could go philosophical on this also: All classes depend on each other to accomplish a common goal; a (hopefully) great software.

So, all in all, dependency or coupling is not a matter of yes or no. It really depends on the context and on a degree of coupling (weak, strong). I thinks that explains why there are some many divergent definition of dependency.

Bryan Menard
  • 13,234
  • 4
  • 31
  • 47
  • Good point, I also think that dependency is rather a degree and not a matter of yes/no (aka binary). But in the context of dependency inversion / static analysis / design principles a la SOLID, everyone uses it in the binary sense, which caused my confusion and this question. – DaveFar Sep 03 '11 at 18:18
  • I think dependency is binary - it's just a question of when it's enforced. Dependency Inversion simply allows you to defer that check until run-time. – Adrian K Sep 05 '11 at 09:54
  • @Adrian K: I disagree. I can easily write software where one has an "optional" dependency. In that case, technically there is no dependency, however a user may percieve this differently as he or she may need it to support a certain feature, creating a functional dependency. – Jonathan van de Veen Sep 05 '11 at 14:28
  • @Jonathan: I think what Bryan meant was that dependency is fuzzy/gradual/continuous. In your example, you seem to have optional, but still binary dependence (ok, optional dependency could be considered as as one additional, intermediate/ternary value). – DaveFar Sep 05 '11 at 15:35
2

I wrote a blog post on that topic a while ago: Understanding Code: Static vs Dynamic Dependencies. Basically you need to make a difference between static dependencies, those that are resolved by the compiler at compile-time, and dynamic dependencies, those that are resolved by the runtime (JVM or CLR) at run-time.

static dependencies are typically provoked by calls to static/final methods, read/write to a field, in the definition of the class C the implementation of the interface I by C ... all these associations between code elements that can be found explicitly in the bytecode and source code.

dynamic dependencies are typically provoked by everything that abstracts a method call at compile time, like calls to abstract/virtual methods (polymorphism), variables or parameters typed with an interface (the implementation class is abstracted at compile-time), but also delegates (.NET) or pointers to function (C++).

Most of the time, when you'll read about dependencies in the literature, they are talking about static dependencies.

A static dependencies is direct (meaning not transitive). A tool like NDepend that I mention in the blog post, can also infer indirect (or call it transitive) static dependencies from the set of direct static dependencies.

The idea I defend in the blog post is that when it comes to understand and maintain a program, one needs to focus mostly on the static dependencies, the ones found in the source code.. Indeed, abstractions facilities are used to, well ... abstract, implementation for callers. This makes source code much more easy to develop and maintain. There are however situations, typically at debugging time, where one needs to know what's really behind an abstraction at run-time.

Patrick from NDepend team
  • 13,237
  • 6
  • 61
  • 92
  • How funny is that: I was just listening to your hanselminute-interview while you wrote this post. I particularly liked your comment that non-linear metrics are too indirect and hence too vague to be useful (did I grasp that correctly?). Do you know of any tool for Java where I can query metrics as in your NDepend (deltas included)? I find that awesome! And thanks a lot for your blog post, I'll definitely read it :) – DaveFar Sep 06 '11 at 08:57
  • I read your blog post and it gave me some insights, thanks a lot :) I do have some questions left, though: the ones I posted as reply to LeleDumbo's post, and whether my examples where the static dependencies graph is not a sub-graph of the dynamic one, given at http://stackoverflow.com/questions/7070570/understanding-compile-vs-run-time-dependencies/7323704#7323704, are true. – DaveFar Sep 06 '11 at 17:28
  • 1
    Yep you grasped it correctly :) Glad this blog post was useful to you. For Java try XDepend.com, aka NDepend for Java. – Patrick from NDepend team Sep 06 '11 at 18:17
  • XDepend looks really great, too bad it requires Windows and .Net Framework :( – DaveFar Sep 06 '11 at 18:51
  • I just marked LeleDumbo's answer as the solution - but your answers where just as helpful. Thanks a lot! – DaveFar Sep 08 '11 at 11:12
1

This post is about static dependency - for dynamic dependency and the difference, see Patrick Smacchia's answer.

In an easy to understand way: an entity (class or package) A depends on an entity B when A cannot be used standalone without B.

Inheritance, aggregation, composition, all of them introduces dependency between related entities.

so there exists no dependency on an interface?

there is, but interface only serves as the glue.

what about inheritance?

see above.

so dependency is a transitive relationship not just on packages, but also on class level?

yep.

but how do you define "relies"?

see above "easy to understand" definition. also related to the 3rd definition you posted.


Update:

So if you have interface A in Package P1, and class C in Package P2 uses A as

  • method parameter, or
  • local variable woven into C via AOP, or
  • class C implements A, or
  • class C<E extends A>,

then C depends on A and P2 depends on P1.

But if interface A is implemented by class B and class C programs against the interface A and only uses B via dependency injection, then C still (statically!) only depends on A, not on B, because the point of dependency injection is that it doesn't make glued components dependent.

DaveFar
  • 7,078
  • 4
  • 50
  • 90
LeleDumbo
  • 9,192
  • 4
  • 24
  • 38
  • If dependency is transitive, that means most classes depend on each other. If `A` depends `B`, and `B` depends on `C`, `A` also depends on `C`? In most cases, I think not. – Bryan Menard Sep 03 '11 at 17:14
  • @Bryan: I find transitivity on class level strange, too. So maybe Martin Fowler's definition should rather be "Class A depends on class B iff changes to the definition of B may cause **direct** changes to A, i.e. not through a ripple effect". – DaveFar Sep 03 '11 at 18:13
  • Do you think that dependency should be transitive at package level? – DaveFar Sep 03 '11 at 18:14
  • @LeleDumbo: So if you have interface A in Package P1, and class C in Package P2 uses A as method parameter OR local variable woven into C via AOP, OR `class C implements A`, OR `class C`, then C depends on A and P2 depends on P1, right? – DaveFar Sep 03 '11 at 18:25
  • And if A is implemented by class B and A uses B via dependency injection, then A still only depends on C, not on B? – DaveFar Sep 03 '11 at 18:27
  • 1
    @Bryan IMO, yes. but in this case, A depends on C implicitly through B. and to prove, you can't build applications with either A or B or even both only. whenever you need A, you need both B and C. – LeleDumbo Sep 03 '11 at 23:11
  • 1
    @DaveBall first question: yes. second question: nope, A doesn't depend on C nor B. dependency injection doesn't make glued components dependent. but maybe I didn't get your point correctly, can you give a concrete example? – LeleDumbo Sep 03 '11 at 23:24
  • @DaveBall, I dont get what you mean: "A is implemented by class B and A uses B via dependency injection". If I understand well A is an interface, per se, it cannot use a class via dependency injection since it doesn't have code in it?? – Patrick from NDepend team Sep 06 '11 at 18:21
  • I don't get what I meant, either ;) I guess I mixed sth. up. So: If Interface A is implemented by class B and class **C** uses B via dependency injection, then C still statically only depends on A, not on B? – DaveFar Sep 06 '11 at 19:07