I have become more skeptical about the use of interfaces in object oriented programming languages like Java.
In particular if there is only one implementing class of an interface.
What's the point of an abstraction if there is only one concrete?
Common arguments go like this, and I don't find them convincing.
You need interfaces to test in isolation
The argument: unit tests should run in isolation, and in particular without accessing I/O and external services. Dependencies to I/O make tests brittle and slow. You need interfaces in order to switch to test doubles - like mocks and stubs - in your test code, enabling isolation .
I think these are some good points. But you don't need interfaces to test classes in isolation. Pass in the dependencies as instances of their concrete class as constructor arguments. Then use a mocking library like Mockito to create the test doubles.
Bottom line: testing in isolation is great, but you don't need interfaces for that.
You need interfaces for changeability
Well known architectural styles like hexagonal architecture promote the use of interfaces, to abstract from the concrete technology and clearly separate infrastructure from domain logic.
When you change your technology decisions significantly, for example move to a different framework, having an architecture based on these concepts will likely pay off. That's why I like the ideas, and have written about them.
Yet, think about this. Say you have only one concrete adapter that implements a certain (driven) port interface. If you consistently used the adapter instead of the port interface, would the application still work?
The answer is: yes. Why shouldn't it. That's not surprising.
But what if you wanted to introduce another implementation of the port interface later on?
Then you could do the following refactoring: replace the concrete adapter class with an interface of the exact same name, make the existing adapter and the new adapter implement the interface. And that's it, your done. Maybe do some renaming if appropriate.
Ok, but why do you have a problem with interfaces?
One thing I don't like about interfaces is that they stop you from "zooming in". In a well maintained code base that consists mostly of concrete classes, you can go from abstract to concrete. Look at the high level algorithms first, then jump to the definitions of called methods by pressing a single key in your IDE. That's the way I gradually increase my understanding of unknown code.
You lose that navigability once you hit an interface. It feels like you hit a wall at first, you have to look up the references and then see how the implementations match. If you have too many of them, the interfaces clutter up your code base and make it harder to understand.
So you hate interfaces?
Not at all. There are good reasons for having interfaces. An obvious one is that you have several implementations in production code that you want to switch based on configuration. Another one is that you want to enforce hard boundaries between the work that different teams do. There are more.
My point is just this: I wish there were more discussions about the detailed practical consequences of certain design decisions, rather than the unquestioned belief in certain design principles. Architecture and design are all about trade-offs: you rarely find a solution that has only benefits, but no downsides. And what works in one context, may not fit another.
What do you think?
Top comments (8)
Interfaces can be overkill, only use them when you're able to clearly articulate a reason (e.g. you expect that you'll need generalization, multiple implementations, etc), not because of dogma or because you've been told to.
And with dynamic (non-typed) languages you would simply use duck typing :-)
Since I've moved away from Java and towards dynamic languages like PHP, Javascript and Ruby I've come to appreciate that a lot of approaches in Java land are incredibly over engineered and overcomplicated, YAGNI syndrome, "patterns" even when you don't need them ... replace that with KISS - simple does it :-)
Object Design is complicated, but necessary for large projects requiring many objects. It also helps the programmer on thinking on security at the object level.
Even thought functional projects can achieve high security standards, the code itself tends to be more open. The object oriented approach also hides and protects the code from other programmers, or at least the implementation. It's a different animal.
However, functional programming gives way more control and flexibility to the Developer. It's easier to tackle logical problems with functional programming.
OOP is all about encapsulating everything. Functional programming makes use of loose variables. This can be extremely useful even thought many OOP Developers resent it so much. Anyway, most functional languages can also make use of OOP, yet they are not really based on objects as JAVA where everything is an object and you can only output from main.
Interfaces can be extremely useful like everything, when it is use properly. It's all about applying good design.
Must admit that for some time I've switched to non-traditional approach to Java code. In my approach interfaces are used to express whole API via few methods which then implemented in the implementation class. This solves few problems:
In general case interfaces are necessary to define class API. Each implementation is written as if only interfaces of other classes are present. This way we clearly separate API and its implementation. It's not even about testing. It's more about keeping code clean from implicit hidden dependencies on details of implementation of the class we're working with.
If that works for you and everybody else on your team - Iβm not against it. Itβs quite common to separate API from implementation for modules or services.
Still, to achieve what youβre describing, you donβt need an interface (if you only have 1 implementation). A properly encapsulated class, with most methods private, and a decent Javadoc documentation would be enough. And thatβs my point.
My point about βhitting the wallβ is not about depending on implementation details - youβre completely right, that should be avoided, and can be, whether you use interfaces or not.
Itβs a statement about gaining a deep understanding of a shared codebase - which would involve your code and the code it depends upon.
From my point of view interfaces have significant advantage: they are free from unnecessary implementation "noise": fields, constructors, private constants, etc. This allows to keep code simpler and cleaner to read. This is especially true for library/framework code often heavily optimized for speed and/or space. Such a code somewhat hairy and hard to read and understand.
My current code style is significantly shifted towards interfaces and often I don't have even implementation classes and everything is put into interface.
Your point about navigability is a great observation.
Personally I think the ability to read and comprehend code is one of the most important things.
There are cases where itβs somewhat unavoidable, but generally people are often far too quick to give up this ability for marginal gains elsewhere.
I think you are correct in that when you only have one implementation of a interface, then perhaps that interface isn't required. I have seen situations where that's the case. Ex The developer made interfaces for each concrete service class. In my opinion, this just adds more clutter.
I get the problem you are saying too and experienced that issue as well. If you absolutely know a particular instance will always be a certain concrete class, then declaring the variable as the concrete class is more meaningful.
Like you said, there are good reasons for interfaces. Interfaces are just another tool in your arsenal to write good code and enforce certain standards across the team.
I believe interfaces to be fundamental aspect of object programming, where upon design, I can explore the conversation one object should have with another yet to be named object. In this case, I am not prevented from fleshing out the former object as I apply the exchange handles (methods) to the latter as an interface. The results may yield that the once anonymous object does indeed speak the language associated with it by interface, but because of the state it manages, it also may have other conversations that, to avoid noise, are not exposed in every interface it implements.
All that being said, I believe object programming (i.e., OOP) is often abused, such that common OOP developers don't know the difference between a function, procedure and method. This is why you have template factories like Spring, molding the art form of software design in to a sophisticated switchboard.