.comment-link {margin-left:.6em;}

Saturday, October 22, 2005

Interfaces or Classes in APIs?

Last week some colleagues and I were debating the use of interfaces vs. classes in API design. Believe it or not, this conversation started because "it had been decided" that we would not adopt the convention of beginning interface names with the letter 'I'. The reason was that this was redundant because all arguments and return values for all API methods for a service should be interfaces and not classes. And so the debate began. Should we use interfaces or classes?

I think the answer is both. Value objects passed into methods or returned from methods can be either classes or interfaces. If you assume that the service (API) you're designing can itself have different implementations, then you should use interfaces for parameters that can have different implementations that are tied to specific implementations of that service. However, in some cases, a method will include a parameter that is a simple "struct" that might be used to modify the behavior of the method. For example, for a "lister" method, you might pass in an object that determines how many values should be returned or whether you want a deep listing of a hierarchical structure. These parameters might as well be defined as concrete classes. There's really nothing to be gained by allowing alternate implementations here.

In general, I like the pattern in which you use a factory to get a specific implementation of a service. That service implementation then acts as a factory that returns other objects that might also be used as factories for additional objects. A good example of this is the Eclipse resource hierarchy. You start by getting an IWorkspaceRoot from which you can get other containers such as IFolder or IProject's from which you can get IFolder's or IFile's and so on.

And yes, I'd prefer to prefix interface names with 'I' even if in some cases it's redundant.

I can't wait for the debate on Collections vs. Arrays...

One problem with using interfaces is that you can't really prevent someone from implementing them even when they are not intended to be implemented by 3rd parties. The trouble with 3rd party implementations is that you can't change the interface after you've defined it without breaking binary compatibility.
Two points:
1. If you expect or encourage third-party implementations of your interface, you should also provide a stub implementation that 3rd parties should subclass. That way, you can extend the interface without breaking them. If they choose to just implement the interface and you break them, it's their issue.

2. Even if you define all of your services and value objects using interfaces but you're still stuck using classes for exceptions. Just look how the DOM API was defined. Lots of interfaces and a single class: DOMException.
I also think the answer is both. There are cases where you want the 3rd party to provide the whole implementation (use an interface); there are cases where you want to make certain there is only one implementation (use a class and perhaps discourage subclassing). There might even be cases where you want to provide most of the implementation but require the 3rd party to provide some of it (use an abstract class?).

We have lots of tools for different purposes. The challenge is often to figure out the best tool for the job. Rules like "always use interfaces in public APIs" just discourage critical thinking.
Re: Dave's cmments.

One tricky case we ran into with a pure factory/interface-based model is when someone wants to subclass your implementation. They have to implement your entire interface and delegate to your implementation in code since they can't directly subclass. There's a way to make this a little easier with dynamic proxies but a lot of people wold probably be uncofortable with that approach.
Post a Comment

Links to this post:

Create a Link

<< Home

This page is powered by Blogger. Isn't yours?