Wednesday, November 10, 2010

When to use interfaces instead of classes

A few years ago, I started seeing developers use interfaces instead of classes in a lot of places. This is a good thing, because it has taken a long time for many developers to really understand object-oriented programming (OOP), and a lot of programmers (myself included) were disguising imperative code with just enough OOP to make things compile. Unfortunately, in many of these scenarios, the technique was being abused or overly used.

Here's a look at some of the situations where it makes sense to use interfaces instead of classes, and where is does not. But first, I'll explain what an interface is at the simplest level.

What exactly is an interface?
An interface defines common functionality across unrelated classes. For example, all sorts of classes that look nothing like each other may have the need to safely get rid of the resources they use.

The IDisposable interface (the name for it in .NET) defines a Dispose() method, which classes then implement. A programmer using those classes knows that, if a class implements the IDisposable interface, the Dispose() method is used to safely release resources.

An interface is a guarantee that certain functionality will work in a standardized way. When properly fashioned, an interface encompasses only the bare minimum for the defined functionality. IEnumerable (which is used to iterate over sequences, lists, sets, etc.), for example, does not provide for a Count property, because it is not concerned with "how many?" it is only worried about "the next one in the sequence".

APIs
I believe you get the most advantage from using interfaces as often as possible when you are writing an API. In an API, you want your code to be as loosely coupled as possible in terms of input and output; after all, you have no control over what the consuming application will need.

There may be a time when it is tempting to work with a List; within a piece of code, and then just return the List, for example. The consumers of the API usually do not need all of the functionality of List, and you can output IEnumerable; instead. This way, if the consumer wants a List, they can have it, or they can have an array of T, or whatever else they might need. By using the common denominator interfaces in your API, you free the consumers from being forced to use or convert to/from your class of choice.

Tightly coupled code
In the fantasy land of Computer Science courses and the blogosphere, developers never write tightly coupled code. In reality, tightly coupled code is a fact of life for the average developer.

You aren't going to make an abstract class and implement the Factory pattern to "future proof" yourself against changes when writing a three line of code class will give you a strong type to shuffle data around. Likewise, coding to interfaces is impractical in many situations; a key sign of this is when you find yourself writing classes that look a lot like structures.

If the only or the best way to get something done is to tightly couple code, then trying to abstract some kind of common functionality into an interface is either doomed to fail or merely more effort than it is worth.

Future proofing
Working with interfaces can give you a measure of future proofing when you are on the consuming end of things. If you are using a library that outputs interface instances instead of classes, not recasting those instances as classes can protect you from changes down the road. Along the same lines, if you only need a certain subset of functionality contained within an interface, working against that interface is better than working against the class.

Go back six or seven years before we had generic types--if you had coded everything to take arrays or Hashtables instead of IEnumerable or ICollection, then if the library ever changed to use List or Dictionary you would have a long road ahead of you to convert over.

Code clarity
Coding with interfaces can improve code readability. How? Because there are fewer "moving parts" for the reader to keep in mind. When you work with an interface, it is more clear what your intentions are and what the capabilities will be. Code is more self-documenting when working with interfaces for this reason.

Sometimes an interface just won't do
Some folks take the use of interfaces to an unworkable extreme. Avoid this at all costs. There will be times when you simply cannot boil down the essence of a variable or a method parameter (or whatever) to an interface. In fact, this is more likely than not. Don't try to force the issue; it's better to just use a class and move on with your life than to try to make things work with an interface.

No comments: