152

At the company I work at, every service class has a corresponding interface.
Is this necessary?

Notes:

  • Most of these interfaces are only used by a single class
  • We are not creating any sort of public API

With modern mocking libraries able to mock concrete classes and IDEs able to extract an interface from a class with two or so clicks, is this just a holdover from earlier times?

Bob Roberts
  • 1,847

6 Answers6

125

Given your question I assume that the reasons for this kind design are not documented.

Unjustified usage of an interface with single implementation is plain wrong since this violates YAGNI. In my experience, this also has been pretty damaging maintenance-wise, mostly because methods implementing interface are forced to be unnecessarily public. After you gather more refactoring experience, you'll probably learn to appreciate modest charm of private and package private access modifiers allowing one to focus attention within a single class / package.

As for undocumented justified usage of this idiom, it is in my eyes about as bad - first of all because it doesn't allow a maintainer to "tell good from bad". As a result, this leaves one between not quite appealing options. Options are, either you keep the questionable public stuff as-is, thus repeatedly decreasing productivity every time you need to change the code, or you make risky changes by blindly "collapsing" the single-implementation into easier to maintain POJO - exposing self to the risk to painfully discover that this is wrong way, that some other (God forbid, remote) module out of your sight expects the interface.

I've been through "revelations" caused by erroneous collapse of single-implementation-interface few times. Each time it has been quite an educative experience but I would not really want to get there again. Thing is, this happens at late testing, at integration or even at user acceptance and rolling back / fixing the mistake made long time ago at this stage is quite cumbersome.


  • An option that shouldn't be left without mentioning is to investigate whether the undocumented use is justified (and, after that, collapse or document respectively) right at the moment you find it.
     
    To me, this one has been quite counter-productive. This approach essentially forces one to go full-round integration testing which is probably one of the most efficient ways to break a flow in the middle of some complicated fix / refactoring.
     
    https://i.sstatic.net/tBUBG.jpg

I just couldn't see the practicality... the interfaces are used one-to-one like com.company.service.foo and com.company.service.foo.impl

Well below is about how I typically handle cases like that.

  1. Create a ticket in issue tracker, like "PRJ-123 Undocumented questionable use of interface with single implementation in Foo / FooImpl".
    Add to ticket description or to comment an explanation that this hurts maintainability and point out that without documentation this looks like overengineering. Add a comment suggesting to fix the issue by "collapsing" this into a POJO, for easier maintainability.
  2. Wait for some time just in case if someone comes to explain things. I'd wait a week or two at least

    • If someone explains the purpose, a) put that to the ticket, b) add to Foo javadocs something like purpose of interface explained in ticket PRJ-123, c) close the ticket, d) skip next three steps.
    • Otherwise, ...
  3. Come to team lead or manager to ask what would they think if I fix the ticket as suggested.
    Pick an adviser having sufficient authority to stand the "scream test" in case if suggested fix turns out wrong.
  4. Collapse to POJO as per suggested fix, arrange code review if that's possible (in slippery cases like that it won't hurt to cover your back).
  5. Wait until the change passes the full testing and update the ticket depending on its outcome - add the information on whether the change passed testing or not.
  6. Create and handle a new issue tracker ticket to deal with remaining similar cases based on the outcome of your pilot ticket (PRJ-123): either document the purpose, or collapse to POJO.

gnat
  • 20,543
  • 29
  • 115
  • 306
122

Your company is following the SOLID principles and targeting an interface rather than concrete class adds zero overhead to the program. What they're doing is a very moderate amount of extra work that can pay back volumes when the assumptions that you are making end up being false...and you can never tell what assumption that's going to be. What you're recommending while slightly less work, can cost many, many, many hours of refactoring that could have just been done ahead of time by simply following basic OO design principles.

You should be learning from these guys and consider yourself lucky. Wish I worked in such an environment where solid design was a priority goal.

yannis
  • 39,647
56

Here is how Adam Bien think about this question: Service s = new ServiceImpl() - Why You Are Doing That?

That is not only bloat (and the opposite of "Convention Over Configuration" idea), but it causes real damage:

  1. Imagine you get another implementation (thats the whole point of an interface) - how would you name it?
  2. It doubles the amount of artifacts - and significantly increases the "complexity"
  3. It is really funny to read the javadocs on the interface and the impl - another redundancy
  4. The navigation in the IDE is less fluent

and I agree with him.

Maybe your co-workers are still stuck in time with J2EE, because this was necessary when working with J2EE, maybe you can find on the internet old tutorials about hard times in J2EE.

gnat
  • 20,543
  • 29
  • 115
  • 306
Xoke
  • 660
24

Java interfaces aren't just about mockability (though of course, that is a factor). An interface exposes a public contract for your service, without implementation details such as private methods or attributes.

Note that "public" when using internal services (as in your case) refers to the actual users of said services, including -- but not limited to -- other programmers and internal services!

Andres F.
  • 5,159
12

One possible reason for every service class to have an interface is that it makes interactions with sophisticated frameworks (e.g., Spring, JEE) much simpler. This is because Java can simply generate interceptor objects against interfaces (see java.lang.reflect.Proxy); doing so against concrete classes is far more tricky and filled with some non-obvious caveats when working with multiple different classloaders. This holds doubly true if you work with OSGi, where those service interfaces will end up being loaded from a different classloader from the implementations of those services. That (together with the service discovery fabric) in turn enforces very strong separation between each service and its clients; the clients literally can't break the abstraction.

Be aware that even if an IDE can extract an interface from a class with a single click, it doesn't mean that it's necessarily going to extract the correct interface. Intelligence is still needed.

1

It seems like overhead to use Interface initially.. but later on when we need to extend some functionality these Interfaces help a lot.

If you do only concrete implementation you have to change code implementation a lot.. But by using interface you just need to add one new class which implement the contract of Interface (and you can call new class object from the reference variable of Old interface).