Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

The Case of the Dreaded Interface

DZone's Guide to

The Case of the Dreaded Interface

In one developer's experience, people tend to overuse interfaces. Let's see where the confusion lies and how to make your codebase more readable.

· Java Zone ·
Free Resource

Get the Edge with a Professional Java IDE. 30-day free trial.

The Java/C# interface has a lot to answer for. Recently, someone told me that, "You can tell a design is coupled if it doesn't use interfaces." After a few follow-up questions, I realized that they were not talking about coding to well-defined contracts in general, but the Java or C# interfaces in particular. My next question was, "Should you have an interface even if there is only one implementation?" The answer was, "Yes if you think there may be more implementations in the future."

I disagree with this view.

Conflating the interface Keyword With the Contract of a Class

My view is that the keyword interface is an implementation detail and is only necessary when we want to benefit from polymorphism, in particular subtype polymorphism. The problem arises from the fact that we often conflate the keyword interface with the contract of a class. The contract of a class are its public methods and attributes. The following are equivalent in their contract if we only have a single account in the system.

So ...

interface BankAccount {
    deposit (Money amount);
    withdraw (Money amount);
}

public class CurrentAccount implements BankAccount {
    deposit (Money amount) { ... }
    withdraw (Money amount) { ... }
}


... is contractually the same as

public class BankAccount { 
    deposit (Money amount) { 
        ... 
    } 
    withdraw (Money amount) { 
        ... 
    } 
} 


In C#, the interface may be named IBankAccount with a BankAccount as the implementation. The "I" prefix convention really irks me, but I do understand it's a convention, so I will say no more.

Introduce the Interface When Needed

Let's get back to the above example with the scenario where we don't have an interface and just one class called BankAccount. At this time, we don't need to differentiate because we only have one kind of Bank Account in the system. When we have another account that differs in behavior, then we can create the BankAccount interface and have two implementations: CurrentAccount and, say, SavingsAccount. Only the construction will change at this point — the contract will not. Even if we started with the BankAccount interface in the first place, the construction would change to add the new account type. You can introduce the interface at the refactoring stage of your TDD cycle just before you add the second implementation of the Bank Account.

Conclusion

In summary, indiscriminate use of anything is usually not a good approach. Interfaces with single implementations are not necessary. I've seen codebases where there is a proliferation of interfaces with one implementation. In some cases, certain libraries and frameworks necessitate the need for an interface even when one is not needed. I would seriously consider different alternatives to these libraries and frameworks. Of course, you may not have an alternative.

The contract of a class is composed of its public methods and public attributes. An interface is a handy way to make that contract more explicit, but it is not necessary. It also makes these codebases more difficult to navigate because there is another layer of indirection. Of course, it is necessary when we have multiple implementations of a contract, and that is the place to use interfaces.

A place where an interface may exist with a single implementation is when we want to invert the dependency to explicitly mark an architectural boundary. For example, in a Hexagonal Architecture, we would not want the application (i.e. core domain) to have a direct dependency with the infrastructure or external services. Here, we may use an interface to invert that dependency. However, I'd only do this in a system where architectural boundaries are well-established.

If you want to minimize the creational change on the client side, then introduce a factory method, builder, or another creational pattern. Of course, that in itself is also another layer of indirection, so make sure that the introduction of a creational pattern is worth the cost. A possible example is where you provide a library and do not have control of the client(s) to this library. A creational pattern here could serve to make the change from class to interface backward compatible for the client.

This post was first published on Codurance.

Get the Java IDE that understands code & makes developing enjoyable. Level up your code with IntelliJ IDEA. Download the free trial.

Topics:
interface ,java ,c# ,coupling

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}