November 28, 2007

...Learn TDD with Codemanship

Unifying Package & Type Dependency Metrics

After a good night's trying to get some sleep, my thoughts on yesterday's half-baked idea have crystallised a little.

I maintain - without proof, I should add - that dependency management works pretty much the same at all levels of code organisation. It seems messy to me, for example, to have class coupling and cohesion metrics displayed with package coupling and cohesion metrics that are, seemingly arbitrarily different.

At the class level, we concern ourselves with metrics like Lack of Cohesion of Methods. Whereas, at the package level we concern ourselves with Relational Cohesion. These are two really quite different ways of measuring fundamentally the same thing - namely, the extent to which things that depend on each other are lumped together into some unit of code organisation.

LCOM (in the Brian Henderson-Sellers version of the formula), measures the average number of methods accessing each attribute of a class, but then does some pretty wierd jiggery-pokery to turn that number on its head. It also does nothing to prevent us from dividing it by zero, which suggests computability wasn't a consideration in the design of this computer metric.

Things are much simpler at the package level. Relational Cohesion measures the average number of relationships between types in the same package. That makes sense to most people, and I don't have a hard time explaining it. LCOM, on the other hand, tends to get blank expressions when I try to explain it.

And what about the Stable Abstractions design principle? We've seen it tackled at the package level - we should prefer to depend on packages that are more abstract. But I think my recent experiments with change propagation in simulated type systems demonstrate quite clearly that it would apply at the type level, too (provided we accept the assumption that more abstract types are less likely to change, which still needs some thought - but if it's not true, then it's not true at the package and the type level, surely?)

And I suggested yesterday that abstractness of types might not be as two-valued (abstract or not-abstract) as we tend to treat it. If a package is as abstract as the proportion of abstract types that it contains, then surely a type is as abstract as the proportion of abstract members that it contains. No?

So an interface with 5 methods, none of which have an implementation, would be 100% abstract and a class with 5 implemented methods would be 0% abstract. And an abstract class with 2 abstract and 3 implemented methods would be 40% abstract.

And since types can have efferent and afferent couplings (outgoing and incoming dependencies), then we can calculate the distance from the main sequence at the type level as well as the package level.

This raises the very real possibility that essentially the same metrics could be applied for coupling and cohesion at package and at type level. Which, at the very least, would be easier to explain. But I do believe that it hints strongly at a more consistent, unified model of dependency management, which is very encouraging.

And with a bit of tinkering, these type-level metrics would be easy to automate using existing technology, which is even better news.

So why don't I have an initial stab at defining these metrics? Well, here goes nothing:

Type Relational Cohesion = Number of Relationships Between Members / Number of Members
(where number of members > 0, otherwise Type Relational Cohesion = 0)

Type Abstractness = Abstract Methods / All Methods
(where all methods > 0, otherwise Type Abstractness = 0)

Type Instability = Type Efferent Couplings / (Type Efferent Couplings + Type Afferent Couplings)
(where types have at least one coupling, otherwise Type Instability = 0)

Type Normalised Distance from The Main Sequence = | Type Abstractness + Type Instability - 1 |

Take the example of the Account class. It has 1 efferent and 4 afferent couplings (a bi-directional association with Customer = 1 efferent + 1 afferent, and then the 3 subclasses make a total of 4 afferent couplings), 2 abstract methods and 4 methods with implementations.

Which all looks rather reasonable to me - based on this one toy example. The next step might be to try it on some real code.
Posted 14 years, 3 months ago on November 28, 2007