An Interface Taxonomy
I generally subscribe to the notion that a huge (and almost universally underrated) part of being a software developer is coming up with good names for things. The importance of naming concepts ranges from the macro-consideration of what to call languages or frameworks (how about “.NET” and “javascript” as preposterously confusing naming-fails) to the application level (I’m looking at you, GIMP for Linux) all the way down to the micro-level of methods and variables:
//u dnt typ lk this if u wnt ppl 2 undrstnd u, do u?
public string Trnstr(string vl2trn)
{
var t_vrbl_apse = GtTrncStrn(vl2trn);
return t_vrbl_apse;
}
So in the interest of clarifying terms and concepts that we discuss, I’d like to suggest a taxonomy of interfaces. As I’m defining them, these terms are not mutually exclusive so a game of “which kind is this” might yield more than one right answer. Also, it almost goes without saying that this is not comprehensive (the only reason I’m saying it is as a disclaimer 🙂 ). I’m really just trying to get the ball rolling here to establish a working, helpful lexicon. If you know of some kind of already-existing taxonomy like this or simply have suggestions for ones I missed, please weigh in with comments.
Characteristic Interfaces
Characteristic interfaces are interfaces used to express runtime capabilities of a type. Examples in C# include ISerializeable, ICloneable, IEnumerable, etc. The author of the class is pulling in some behaviors that are ancillary features of the type rather than the main course. You might have a Customer object that happened to be cloneable and serializable, but those facts wouldn’t be prominently mentioned when you were explaining to someone the purpose of the object. You’re likely to see these as method parameters and return types since this is a favorable alternative to casting for expressing interest in some set of objects with features in common.
Client Interfaces
Client interfaces are used to guide users of your code as to what functionality they need to supply. This might include an interface called IValidateOrders with a Validate() method taking an order parameter, for instance. Your code understands orders and it understands that they need to be validated, but they leave it up to client implementations to supply that validation logic. With the fall of popular inheritance structures and the rise of composition, this has become a much more common way to interact with arbitrary client code.
Documentation Interfaces
This is a type of interface that exists mainly for write-time documentation rather than any kind of build or run-time considerations. You see this in situations where the actual implementation of the interface results in no runtime polymorphism, unit-testing, or flexibility concerns. The interface is defined simply so that other developers will know what methods need to be written if/when they define an implementation or else to provide readability cues by having a quick list of what the class does to the right of a colon next to its name. An example would be having Car : IMoveAround where the interface simply has one method called “Move()”. Obviously, any documentation interface will lose its status as soon as someone introduces runtime polymorphism (e.g. some kind of class factory or configurability with an IoC container), but you might describe it this way up until that point or, perhaps even after, if it was clearly intended for documentation purposes.
Identify Interfaces
Conceptually, these are the opposite of Characteristic interfaces. These are interfaces that an implementing type uses to define itself. Examples might include implementers of IRepository<T>, IFileParser, or INetworkListener. Implementing the interface pretty much wears out the type’s allowance for functionality under the Single Responsibility Principle. Interestingly enough, it’s perfectly reasonable to see a type implement an Identity Interface and a Characteristic Interface (service classes implementing IDisposable come immediately to mind).
Seam Interfaces
A seam interface is one that divides your application in swappable ways. A good example would be a series of repositories that make use of IDataAccess implementations. The dependency of repositories on schemes for accessing data is inverted, allowing easy testing of the repositories and simple runtime configuration of different data access schemes. As a concrete example, an architecture using a seam interface between repository and data access could switch from using a SQL Server database to a flat file structure or a document database by altering only a few lines of wireup code or XML. Seam interfaces provide points at which applications are easy to test and change.
Speculative Interfaces
Speculative interfaces are sort of the dark side of seam interfaces–they’re unrealized seams. In our previous example, the IDataAccess interface would be speculative if there were only one type of persistence and that was going to be the case for the foreseeable future. The interface is still providing a seam for testing, but now the complexity that it introduces is of questionable value since there aren’t multiple implementations and it could be argued that you’re simply introducing complexity in violation of YAGNI. It’s generally easiest to identify speculative intefaces by the naming scheme of the interfaces and their single implementation: Foo and IFoo, LookupService and ILookupService, etc. (This latter naming example is specific to C#–I don’t know exactly how people commonly name speculative interfaces in other languages or if there is a consistent naming scheme at all, absent the C# specific Hungarian notation for interfaces.)
Yeah, as I read somewhere sometime: There are two hard things in computer science: cache invalidation, naming things and off-by-one errors.
🙂