When Do I Use Interfaces?

Monday, June 8, 2009

interface “Program to an interface, not an implementation” is a well-known mantra from the GoF book. Take this guidance to an extreme, though, and you generate POO instead of OOP. How do know if you crossed the line?

I think it’s useful to take a step back and think about the word “interface” in a general sense. There are interfaces everywhere in software.  There are interfaces between layers, between tiers, between applications, between objects, and between callers and their callees. Just about anything and everything in software, no matter how trivial, has an interface.

The real question with interfaces is how many constraints you want in place for any given interface. Consider the following JavaScript code.

function validate(creditService) {
    creditService.checkCreditForCustomer(this.id);
}

The only constraint on the creditService parameter is that the object needs a checkCreditForCustomer function that takes an ID parameter. The validation function doesn’t care how the creditService was built, who built it, where it came from, or what other capabilities might be in place. This code demonstrates the flexible, dynamic, and relatively unconstrained qualities of duck typing. If the parameter checks the credit of a customer like a credit service should, then it must be a credit service.

Going Static

Static languages generally have to crank up the constraints on an interface, although many have an escape hatch. C# 4.0, for example, introduces a dynamic type.

public bool Validate(dynamic creditService)
{
    return creditService.CheckCreditForCustomer(ID);
}

Again - all we need is an object with a CheckCreditForCustomer method that takes an int parameter. Because the object is typed as dynamic, the compiler won’t guarantee what the object can actually do – there is no type checking. At runtime, we may find out the object doesn’t actually support the method we are looking for, and an exception appears. This duck typing behavior is what keeps fans of static typing awake at night. They think the dynamic programmers are insane for throwing around objects in a willy-nilly manner. Meanwhile, the dynamic crowd thinks the fans of static typing are insane for spending all of their time obsessing over types instead of creating software.

Regardless of where you fall in the static to dynamic spectrum, you can view a type definition as a constraint. In C# and Java, the interface keyword can constrain the type of an object without placing any constraints on the implementation.

interface ICreditService
{
    bool CheckCreditForCustomer(int id);
    bool CheckCreditForCompany(int id);
}

Now we can use this constraint to enforce type safety.

public bool Validate(ICreditService creditService)
{
    return creditService.CheckCreditForCustomer(ID);
}

An interface (in the interface keyword sense) allows fans of static typing to sleep at night while still leaving some flexibility behind. The object that arrives as an ICreditService on any given call might be one of 10 different credit service implementations. The 10 implementations may be from the same class inheritance hierarchy, or they may not. One might be a mock object or test double used only during testing (which I should point out is not, not, not the point of using interfaces), or it may not. The Validate doesn’t care about the concrete implementation behind the interface.

We still have some flexibility, but we also have additional constraints when compared to duck typing. The credit service has to implement two methods now, even if we just want to build an object for the Validate method which only uses the CheckCreditForCustomer method. These two methods may or may not be good thing. Iterative design with tests and a dose of the interface segregation principle will take care of the matter.

Going Concrete

Even more constraints come into play if we use a class definition instead of an interface.

public class CreditService
{
    public virtual bool CheckCreditForCustomer(int id)
    { 
        // ...
    }
    
    // ...
}

Now we’ve not only constrained the type, but we’ve constrained the implementation. Whoever provides our credit service functionality must be a CreditService object, or use CreditService as a base class. Building software is all about composing pieces of functionality together, and using a concrete class as the interface specification places hard restrictions on how the composition will work now,and in the future.

Interfaces Everywhere?

Sometimes, these hard restrictions make sense, or at least aren’t important. For example, classes that have no behavior (like DTOs) don’t need an interface abstraction. I’ve also never found it useful to specify entities using an interface, as they have pure business logic inside (logic dealing only with other business objects or abstractions).

public interface ICustomer
{
    int ID { get; set; }          
    int Name { get; set; }
    void UpdateAddress(/* ... */);
    // ...
}
 

In short, you don’t need interfaces everywhere, you need to anticipate where your software needs to be flexible, which isn’t always easy. Using interface definitions between two horizontal or vertical layers of an application is almost always a yes, but programming to an interface between two business objects inside the same context is a definite maybe.

I like to use interface definitions when I want to turn a detail into a concept. For example, I’d feel more comfortable with an business object using an ISendMessage object then an SmtpServer object. The concept is closer to what the object needs to do (send a message), and it’s easier to change the business object’s behavior by giving the object a different ISendMessage implementation. As a special extra double bonus, the object using ISendMessage is much easier to test. List<T> is a detail. IList<T> is a concept.

If you doubt the power of interface programming, then just look at COM. Really. In COM you could only program to an object’s interface, and this allowed objects from different runtimes (Visual Basic versus C), different threading models (objects with a thread affinity versus multi-threaded objects), and different processes (local versus remote) to all work together, plus a host of other features. Interface definitions are the ultimate abstraction (for a statically typed environment!).


Comments
Rob White Monday, June 8, 2009
I'd be interested to know your opinions on how this relates to writing code that is easy to unit test. Would you apply the same thoughts when working with IOC scenarios?
Mike Monday, June 8, 2009
Good post, totally agree about not using interfaces on domain objects I went down this route and ended up with unit tests with mock objects that returned mock domain objects and quickly turned into a maintenance nightmare.

Now I use Object Mother and Test Data Builder patterns in my unit tests and it's much cleaner.
Khaja Minhajuddin Monday, June 8, 2009
Completely agree with the post! It's just pure common sense. But, we see a lot of developers abuse interfaces (even good developers). I remember watching a very good video about TDD and Resharper by a guy(whom I won't name), and he was using interfaces for each and every object. Thanks for doing your bit to create awareness about this.
Roberto Hernandez Monday, June 8, 2009
I love the cutting edge Sketching tool you used for the diagram in this article. :)
Scott Allen Monday, June 8, 2009
@Rob - The nice thing about programming to a concept instead of a detail is that you do end up with more flexible software, and the by-product is that the software is easier to test. After you've done it for some time you'll develop a good feel for where you need the flexibility.

IoC and DI solve the problem on the other side of the coin. Once you have these flexible interfaces defined, how does a piece of software remain completely ignorant of the details when it needs to use something offered by an interface?

From this perspective we are looking at things like ICreditService as dependencies, and injecting these dependencies it a great way to for the consumer objects to remain ignorant of the details and program only to the concept.

All the knowledge about how to manage the details is tucked away into the container and its configuration.
Emmanuel Caradec Tuesday, June 9, 2009
I agree with the overuse of abstract interface beeing annoying. I want to point out that "Program to an interface, not an implementation" does not imply the interface has to be abstract. The hint is still true for a concrete object. From the outside of an object, you should considering it a black box, no matter the interface is abstract or not.
Doeke Zanstra Tuesday, June 9, 2009
The javascript function also needs the 'return' statement. It's not Ruby! ;-)
Shane Stanford Tuesday, June 9, 2009
As an architect and a developer I think that programming to the interface is important but that programming for change is the most important. That should always be your design decision. Is the implementation of this going to change then let put an interface on it.
Comments are now closed.
by K. Scott Allen K.Scott Allen
My Pluralsight Courses
The Podcast!