Maven, an open source project management tool, uses JDepend, a design quality metrics tool. The design quality metrics it purports to measure are the dependencies among modules (in this case Java classes and packages). It is based on Robert Martin's OO Design Quality Metrics: An Analysis of Dependencies (1994). Here is an example of a JDepend report.
What's interesting about Martin's approach is his relationship between abstraction and stability. His claim is that highly stable layers should be abstract, and that highly volatile layers should be concrete. Why? I didn't find this immediately intuitive. Why not have everything be volatile? or stable? Then I thought of it in the following terms. If there is a certain way of doing something that has stood the test of time, e.g., a particular sort module, then it makes sense to make it as abstract (i.e., generic, general purpose) as possible, so that many modules can use it (i.e., depend on it). Conversely, if the best way of doing something is still being actively debated and experimented with (e.g., ontology representation languages), it makes no sense to abstract it for more general purpose use. Such volatile modules should be concretely embodied in their specific domains.
Martin's metrics are interesting because they encourage a spanning layer style of abstraction (i.e., an abstraction layer that abstracts implementation as well as application). Martin gives the example of a copy module that uses two abstract classes (reader and writer) as a good stable abstraction layer because the concrete classes (e.g., keyboard reader and keyboard writer) are dependent on the abstraction layer, not the other way around:
What would make Martin's example even more compelling is to abstract the copy module into a pipeline module, which invoked a transform abstract class. This would enable a diverse set of uses of the pipeline abstraction, beyond just copying; for example filtering, sorting, etc. In fact, this is roughly equivalent to Unix pipes, a spanning layer of great stability. Unfortunately, Martin's metrics don't distinguish between implementation and application abstract classes. I wonder if other metrics do?
Martin calls the switch from a design in which the general (abstraction) depends on (invokes) the specific (instantiation) to a design in which the specific depends on the general a dependency inversion. And he states the following dependency inversion principle:
High level modules should not depend upon low level modules. Both should depend upon abstractions.
Abstractions should not depend upon details. Details should depend upon abstractions.
This is essentially an argument for abstract frameworks (J2EE containers, Microsoft Foundation Classes) over concrete component libraries. Calling a lower level concrete component makes the higher level component dependent on something more volatile. There is a good paper entitled Aspect-Oriented Dependency Inversion that discusses the principle in more depth and relates it to Aspect-Oriented Programming.
This begs the question of when one abstract (general) class is more general than another abstract class, e.g., is copy more or less general than reader or writer? My guess this question is related to the Aspect-Oriented Programming issue of the tyranny of the dominant decomposition. That is, a general/specific ordering is relative to a particular set of concerns. Thus, from the POV of the reader, copy is a specific type of transformation, and from the POV of copy, reader is a particular kind of source.
One other interesting association made by Martin is between abstraction and policy:
However the "Copy" module is not reusable in any context which does not involve a keyboard or a printer. This is a shame since the intelligence of the system is maintained in this module. It is the "Copy" module that encapsulates a very interesting policy that we would like to reuse.
Compare this to the following definition of policy: Policies are rules governing the choices in behavior of a system. Are policies then the highest level of abstraction?