Sunday, August 20, 2006

So You Think You Can Dance

I'm going to use the Foxtrot dance as a metaphor for interfaces.  I am really much more interested in telling you about why you would use interfaces than I am in telling you how to define an interface.  Once you understand the intent of the interface, the mechanics of specifying an interface become quite simple.  Now on with our metaphor.  The Foxtrot defines how two people can dance together.  It is only meaningful when there are in fact two people playing the roles that the Foxtrot defines.  One person leads and the other person follows.  Supply the appropriate music, and any two people who are familiar with the Foxtrot can come together and dance.  One can imagine an evening in which an individual might dance with many partners.  One could even imagine that during a single musical number, that one individual could "cut in" (that is replace one of the partners) in mid-musical number without any necessary disruption.  The Foxtrot is an abstraction which only comes to concrete reality when two dance partners decide to fill the roles defined by the Foxtrot.

An interface is very much like the Foxtrot.  An interface is an abstract promise.  By that, I mean that the interface in and of itself does not do anything but does define what other parts of the software should be doing.  An interface is an expression of capability: "I can do something" or "I have something".  [As an aside, there is a general tradition that the name of the interface should start with the letter I.  There are those who think that that is inappropriate, but I am not one of them.]

Just like the dance, there are two other players in our little drama.  There is a class that implements the interface.  And there is a set of classes that are going to use  or consume the interface.  To keep things linguistically simple I'm going to assume it is only a single class that implements the interface, but keep in mind that the typical use of interfaces involves having many different classes implement the same interface.

The class that implements the interface is declaring that it will keep the promise of the interface.  In most programming languages, the compiler will assist the software developer by requiring the implementing class to provide programming logic for each of the method signatures defined by the interface.  That is, the software developer must provide each of these method signatures in order to compile the software.  The software developer should provide logic that implements the intent of the interface in each of these methods.  As we will see later, is possible to "cheat" the interface by implementing incomplete or empty methods.

The class that uses or consumes the interface is declaring that it will restrict its use of the class that implements the interface to the functionality that is exposed by the interface.  There is really is nothing in the typical compiler that enforces this restriction.  It really is up to the software developer to "play by the rules" and not base any of its programming logic upon the actual type of the class that is implementing the interface.

Why would you use an interface?  The primary reason to use an interface is to create what I call a "flex point".  Just as with the Foxtrot above, we want the ability to substitute a different class instance to fulfill one of the roles.  Typically we are concerned with the substitution of the object that implements the interface but if we take a look at the "big picture" we want to be able to substitute on either side of the interface, both consumer and implementer.  The consuming class instance is saying that, as long as the object that I am consuming implements this interface, I will accept any and all possible class instances.  This allows the control logic within the application to bring together two classes that have promised to obey the rules and regulations of the interface to perform their "dance".  The control logic can switch this around later to adapt to different circumstances.  This "switch" could be dynamic (based upon a configuration option) or could be static in the sense that the control logic is rewritten to reflect new requirements.

An interface defines intent.  Even with modern programming languages, not all of the intent can be included within the code.  The typical programming language, at most, allows the software developer to define the method signatures, that is, the number and data type of the parameters and the data type of the return value.  There is typically nothing that can be said about the contents of the parameters or of the return value other than their data type, nothing that can be said about the preconditions or the post-conditions for each method signature.  ( In a later blog entry, we will talk about some ways by which you can get around this, at least to some extent.)  There is nothing in the typical interface specification that controls the order in which the methods of the interface should be called.  The order of execution of the methods might be critical to fulfill the intent of the interface.  Much of the intent of the interface has to be included in documentation for each method or in the programmers cerebral cortex.  Neither of those are as comforting as explicit specifications that are enforced by some software mechanism.

Even with the compiler policing the implementation of the interface it is still possible for the software developer to fail to keep the promise of the interface.  The software developer might include the method signature to satisfy the compiler but include no useful logic in that method.  The method might return a null value rather than a value that keeps the promise of the interface.  The method might even throw an exception to indicate that the method has not been implemented.  All of these things are bad.  It might be an indication of laziness or sloppiness upon the part of the person who is implementing the interface.  It might also be an indication of sloppiness or laziness on the part of the person who defined the interface.  As we will see later, a well-defined interface should be sharply defined and narrowly focused.  A well-defined interface should eliminate the need and even the opportunity to include such dummy logic

Now that I have given you some idea of the context of using an interface, the next step is to define an application domain in which an interface might be profitably used.  That's our next blog entry: To Document or Not to Document?

 tags: , , ,


Post a Comment

Links to this post:

Create a Link

<< Home