DEV Community

Tom Smykowski
Tom Smykowski

Posted on

Should you always code against an interface?

Lately I had a rather interesting discussion about interface-based programming called also coding to an interface, or programming to an interface.

It is a popular pattern among Java developers:

Interface-based programming, also known as interface-based architecture, is an architectural pattern for implementing modular programming at the component level in an object-oriented programming language which does not have a module system. An example of such a language is Java, which (as of 2015), does not have a module system at the level of components. Java has a package system, but Java software components typically consist of multiple Java packages – and in any case, interface programming can provide advantages over merely using Java packages, even if a component only consists of a single Java package.``
source: https://en.wikipedia.org/wiki/Interface-based_programming

It is a well-known and rather good pattern. Advantages are well described on the internet, for example Sylvia Fronczak quotes famous Design Patterns:

Clients remain unaware of the specific types of objects they use, as long as the objects adhere to the interface that clients expect.

Clients remain unaware of the classes that implement these objects. Clients only know about the abstract class(es) defining the interface.``

sources: https://blog.ndepend.com/programming-interface-simple-explanation/, https://en.wikipedia.org/wiki/Design_Patterns

What it means is that you can have multiple classes implementing the same interface, and one code you wrote to handle an interface should work for all classes implementing it. Sylvia wrote a great summary why you should use it:

Interfaces make your code less brittle. If implementations change, your code will still work—as long as the interface doesn’t change.

However, there is an urging question, when programming against an interface pattern should be used. There are some interesting answers to that question:

Sylvia Fronczak underscores the need to find a balance when it comes to using the pattern:

You need a good balance when deciding what interfaces to use, how many, and at what abstraction level. With practice and the right tools to measure your progress, you’ll be more confident with your architectural choices.

Philip Brown puts some pressure on having many child classes:

You should use an Interface when you will have many child classes that should all be capable of doing certain actions
source: https://culttt.com/2014/04/02/code-interface/

Ivo Coumans points out it is a guideline:

If you follow the guideline to "program to an interface, not an implementation", you'd be violating the KISS principle. But by making a bunch of simple classes that repeat some code or actions, you'd be violating the DRY principle.
source: https://softwareengineering.stackexchange.com/a/402034

Tor Hovland lists out situations when coding against an interface is good, mostly:

When they add value, they are useful. I’m only saying that interfaces that mirror one and only one class implementation is waste.
source: https://blog.hovland.xyz/2017-04-22-stop-overusing-interfaces/

Jeroen Vannevel points out also a general rule he finds is good:

(...) you should define your program against the lowest possible abstraction without losing functionality
source: https://softwareengineering.stackexchange.com/a/283763

If it comes to the conflict with KISS and YAGNI danielorn notes out that the conflict can even not exist:

It is simpler (KISS) to program against a generic interface than a specific implementation.
If don't need a specific implementation, you should use the generic interface (YAGNI) source: https://stackoverflow.com/a/65874938/38940

What do I think?

Programming against an interface is a good pattern. As with all patterns we should indeed balance the benefits it gives us. Definitely every programmer should know this pattern and recognize a situation when it is needed.

Moreover, there are some types of projects, and some languages that make it more useful to use it. Just to name Java, or C#. In such projects using coding against an interface pattern can be the main way of doing stuff. And some companies, and teams can decide that the pattern is a standard for them. And it is fine.

However, it is not true for all projects, cases and languages. Let's take an example from my StackOverflow question. If I have a short piece of code that does nothing but uses a temporary variable. Should I use:

Map<Integer, String> langMap = new HashMap<>();
Enter fullscreen mode Exit fullscreen mode

or

HashMap<Integer, String> langMap = new HashMap<>();
Enter fullscreen mode Exit fullscreen mode

?

According to the programming against an interface pattern with the first line, I am saying I will use only features contracted by Map. However, the second line tells I will use features of HashMap. So, based on the pattern I should go for the first variant. Because at this point of time, I don't consider to use HashMap specific features. It makes perfect sense.

But it assumes I follow programming against an interface pattern in every situation. And the question of the post is if I really should do it?

In the example above, when I know I won't use any features the pattern gives, it does not make too much sense to use the pattern in the first place. It is a possibility some people can reject right away, assuming not using the pattern is a sign of bad practice.

In fact if we think about other patters, we don't always use them. We don't write Factory Methods, Builders, Facades and Proxies for every chunk of code. We write them, when these are important for the code quality.

As with all patterns using them for no reason leads also to some conclusions:

1) The person who uses them, does not really know anything about them
2) As a new level of abstraction it increases unnecessary code complexity

In my 17 years career of a software developer I have seen more problems caused by people using things they don't understand, and in a result complicating projects, than the other way around. With time, it seems accidental complexity is a growing issue that increases maintenance costs and risk of errors.

In fact, I'd rather see people not use programming against an interface pattern when it is not needed to not raise suspicion they don't know what they are doing.

Conclusion

As a developer you should be aware what patterns to use and why you use them. Your environment, team, or project may require using the coding against an interface pattern always, even if there is no benefit. Then, you may decide to follow the rule, if it does not have worse side effects.

But also, if you decide knowingly to not use the pattern, because there are no benefits in a particular situation, it is also fine.

As long as you are aware of what you are doing, both ways are fine.

What do you think?

Is there anything I have missed about the subject? Do you strongly disagree with something? Have different opinion? Write a comment! Also remember about the Code of Conduct (https://dev.to/code-of-conduct)!

Top comments (6)

Collapse
 
brian1150 profile image
Brian-1150

Thanks @tomaszs2 ! I have been noticing a pattern where there is a one-to-one relationship between the interface and implementing class. And when this is done 50 times in a project, it seems to me that there was a lot of unnecessary code and the interfaces are not serving a purpose.

Collapse
 
tomaszs2 profile image
Tom Smykowski

Thanks for the comment. What is your experience of working with such code?

Collapse
 
brian1150 profile image
Brian-1150

My experience is actually with C# code in .NET Core, and very little Java but the concepts are very similar.

Collapse
 
bertilmuth profile image
Bertil Muth
Collapse
 
alainvanhout profile image
Alain Van Hout • Edited

My view (similar to yours, it seems): adding an interface means adding complexity. That complexity will have benefits in some cases, but outside those cases all you have is the cost of the abstraction. And as has been said, everything can be solved by adding another layer of abstraction, except having too many layers of abstraction.

So the question becomes: in your case, does it offer an actual, concrete benefit, to offset the cost of added complexity?

Collapse
 
tomaszs2 profile image
Tom Smykowski

Thanks for your insight. Ps. I love the saying about solving things with abstraction :-)