A dependency graph is often used for appreciating/understanding the design of a (statically typed) program. The idea is simple: take every class in your program and create a corresponding graph vertex. Then, for each class X that uses a class Y create a graph edge between the corresponding vertices.

This graph can then be presented in various formats: a diagram, a list of pairs (of class names), etc. It is common to check the existence of cycles in a program's graph. Cycles are usually considered to be evil, so programmers are often complemented for developing cycle-free programs.

Having presented the background, I'd like to make two quick statements on this topic:
  1. Self dependency. Every class (and I mean every last one of them) always depends upon itself. Why? you cannot shield a class from changes in itself. For example, if you remove a private filed (let alone a public one) from a class, C, which classes will be affected? Right, C itself.

    It is not rare to see programmers who think that a class will depend upon itself only if it has a recursive method or a a recursive structure. This is a common fallacy. Every class has self-dependency, even if its methods are not recursive.

  2. Cycles. Suppose you have a cycle of two classes, that is: X depends on Y and Y depends on X. The standard trick for breaking the cycle is to extract an interface Z out of Y. Then make X use Z instead of Y. You may now think that X is no longer depending on Y.

    Now, ask yourself this question: "If you change something in Y, will X not bleed?". The answer is: Yes. When your program is running, X will still invoke Y's methods (through the Z interface) and if these methods behave differently, X will be affected. In other words, the introduction of Z eliminated the compile-time dependency but not the run-time dependency.

    Am I saying that there's no point in detecting compile-time cycles? No. Compile-time cycles are important but for a different reason: reusability. If X and Y are cyclic, then you cannot just take X and reuse in some other program/module. You will always have to carry class Y as a baggage (and vice versa). This restricts reusability. On the other hand, if X only depends on the (extracted interface) Z, then you can take only X and reuse it just by plugging-in some implementation for Z.

0 comments :: Dependencies

Post a Comment