Reused Abstractions & NDepend
If you've worked with me or have read my tutorial on OO design principles, you might recall that I'm not a fan of abstractions for the sake of abstractions. Well, not anymore, at least (I've learned my lesson).
My contention is that the practical purpose of abstraction is to reduce duplication. In the discipline of evolutionary - or feedback-driven - design, of which I most definitely am a fan, we discover abstractions wherever we see code that looks very similar.
A consequence of this guiding principle is that abstractions must be reused. In programming terms, any abstract class or interface must be implemented by more than one concrete class.
So what if they don't?
Well, there are two consequences of not following this principle that have caused me problems in the past:
1. Code clutter - when every object is split into TypeName and TypeNameImpl (stand up and take a bow, my Java buddies!) then you end up scrolling through reels of code files that start with the same string of characters. It just clutters up your projects and makes getting around that much harder, in my experience. "Let me just click on the... Oh hang on, that's just the interface..."
2. It buggers up my Martin metrics - You know what? I actually care if my package dependencies are going in the right direction, that it is, from less abstract to more abstract packages. It's important, y'know. If my package dependency metrics are messed up by packages full of abstractions that aren't doing the job they're supposed to be doing (i.e., abstracting stuff and removing duplication) then I can't get the objective feedback I need to steer my efforts in the right direction.
Whinge! Moan! etc
Anyhoo. When I'm prostituting my services as a "design authority" - a role which surely requires that I wear a deerstalker hat and smoke a pipe - I prefer to be as lazy as possible. Time was that I would painstakingly examine the code, going through every file with a fine-toothed comb looking for any design nastiness like the aforementioned un-reused abstractions.
These days I can't be bothered, frankly. Who's got time to look at code?
Thank heavens for tools like NDepend, then!
You can point it at your compiled assemblies and it will suck out a whole bunch of amazing stuff that you didn't know about your code. Like how big it is. And how many unit tests you'd have to write to get 100% path coverage. And whether or not your package dependencies are going in the right direction.
It also has its own code query language - the imaginatively titled "Code Query Language" (CQL). Using CQL - which, I understand, is currently being beefed up into an even more flexible tool for the next version - I can combine cherry-pickings from NDepend's wide selection of Assembly, Type and Method metrics into new pieces of information, and have the tool raise warnings when my user-defined design principles are being broken.
This little snippet, for example, will warn me about any abstractions that aren't being reused:
WARN IF Count > 0 IN SELECT TYPES WHERE IsAbstract AND NbChildren < 2
You can run NDepend as part of a build script, so it's possible - in theory - to build a server application that would take the output of that and email me about any of my precious design rules that are being broken.
NDepend makes short work of CruiseControl.NET - highlighting un-reused interfaces (curse you, NMock!)
All I need then is another application that bypasses me altogether and sends an email to the developer who checked in the code that broke the rule, saying "Oi! Get it sorted!"...
Better still, hook it up to my refactorbot, and let it sort the problem out while we all go on a jolly.
Now if I could just get it to make me the perfect gin & tonic...