I use interfaces to abstract away the "essential" behavior of a thing. What do I mean by "essential"? Well, when I decide to write a Dog class, for instance, there's lots of things that I can chuck in there...dogs scratch, dig, bark, beg, roll over, etc. Which of these things am I interested in representing in my particular application? For the purposes of my app, I decide which of these features are salient, and then even more, which of them are *defining behaviors*. If a defining behavior is removed, and the thing that's left would no longer be considered a "dog" for the purposes of my app, then it's a defining, or essential, behavior.
I consider if this is likely to remain so over many, many versions of the application, and all possible future uses I can think of, and I even try to consider those I cannot yet possibly fathom. If the behavior is just so essential to the class that it would irrevocably change the nature of the beast if it were removed, then it survives as an essential behavior.
These kinds of behaviors are worth abstracting into interfaces. It is notable to consider that there are and will always be whole classes, indeed whole packages, that have no such defining behaviors and these should be treated as unstable (to use Fowler's very apt terminology), as they will likely change from version to version. But the behaviors that define the nature of the application and its basic purpose should be encapsulated in interfaces, and these interfaces should be set off into a package structure that will remain stable over many versions. This serves as a sort of basic bedrock from which the current incarnation with all its version-specific googahs can be built. This is sort of like an application "framework" (using the
word in a slightly different sense from its usual usage). The bottom line: these are meant to be the long-lived APIs that form the basic functionality of your app. If you find you must change them down the road at some point, it's nothing less than an admission that you misunderstood your business in a fundamental way at the time they were designed. (It is common to see all of the "obvious" functionality that it would simply never make sense to change go in here, and it's even more common to see the amount of code in these packages be very, very small compared to the rest of the codebase--though these APIs typically require much of the time and thought that goes into a project.)
I use interfaces in another way as well that's fundamentally different from this, as a language construct for enforcing contracts, which can be very useful even for a specific version, non-essential kind of behavior (for instance, you might have a listener interface, the purpose of which completely pertains to some very unstable feature of your app). These interfaces would be bundled in packages alongside implementation classes and, more or less, treated as if they themselves were implementation with the proviso that they are, even so, abstractions to some degree. Even these interfaces should be established with the idea in mind that they should be stable at least throughout the development cycle for that particular version (in other words, the "essential" behaviors for that particular version's feature the interface supports should be distilled into that interface and not change). It's often useful along this vein to split off a package that keeps these kinds of "somewhat stable" abstractions separate from detailed implementation. By building up level upon level, you can isolate ripple due to change to the levels that the change actually addresses.
So, the point is, the language construct that is the interface should be used to crystallize aspects of your design or enforce contracts or some other well-defined purpose. To simply say, well, let's go ahead and use it everywhere is just kind of silly if its usage is not supporting some aspect of the design or addressing some aspect of the restrictions under which the code should evolve as time goes on.
sev
[ June 21, 2004: Message edited by: sever oon ]