4

There is a discussion at work about the correct use of interfaces in OOP. I have been taught, and always worked from the premise, that interfaces precede concretions and all methods should be dealing in contracts. This is decoupling 101 to me.

I have found that applying this pattern universally teaches junior devs the ropes, sets me up for success down the road ("oh! all of this is tightly coupled! I can't use any of this!"), and takes very little time. It's simple (and worthwhile) to understand that we always, all the time, deal in contracts.

Another fellow is saying that abstraction should only be applied when there are multiple implementations and that doing it all the time makes things confusing for the team. For me, I don't care ;D it's only confusing at first and quickly becomes second nature, and to me is just the proper way of building.

But I wanted to reach out and see if anyone could provide some references / expert texts explaining why or why not.

froodley
  • 61
  • 1
  • 3

5 Answers5

8

I don't want to even know if I'm using an interface (the keyword kind). My drive function takes a Car. Whether that's concrete or not is not any of its business.

This is why I'm annoyed by C#'s ICar convention. Get that pointless I noise outta my face. Java isn't much better. Oh sure, by convention, I'm allowed to name an interface, abstract class, or concrete class Car in the source code but if I change from one to the other I have to recompile everything that uses it!

All I want is to express what drive()s needs are in the type system. I have no desire for drive() to know what it's talking to beyond knowing that whatever it is, it knows how to listen.

By the way, if you have good tests, a language with duck typing gives you all this for free.

But since I have to use these languages as I find them I tend to use role interfaces in them. Which means I don't write drive(Car car) I write drive(DriverControls driverControls) and whatever wants to accept steering, accelerating, and breaking messages over the DriverControls protocol is free to do so.

So if that's what you're talking about I'm with you. If you're one of those fanatics that insists every class like Car have an ICar counterpart you can go jump in a lake.

candied_orange
  • 119,268
5

I agree with your coworker.

Creating good abstractions is hard. Like, really, really HARD!

Abstractions add complextiy to software. They make it harder to navigate the code and reason about it. They make things harder to change, as changes across abstractions often require changes in multiple places.

An interface that just copies what methods the class has is not a good abstraction. Only time you can tell you have good abstraction is that you can imagine (or actually have) multiple different implementations.

Whenever you are creating an abstraction, you need to ask yourself a question "Is it really worth it complicating the code so I can decouple these pieces?"

Euphoric
  • 38,149
5

You are missing one big piece of the puzzle:

Using interfaces everywhere makes your code much harder to navigate

Assume you have a large codebase with hundreds of classes that has grown over the years.

Usually, when you want to know what happens in a method, you just click on it and the IDE will jump to its code ...that is, if it's a class. If it's an interface, you'll jump to the interface, then you have to figure out what class is instanciated, and this might take a while, and go there, and go inside another of its method and again land on an interface, so you have to figure out again what was actually instanciated, which can take a while, etc.

Basically, navigating the code become increasingly frustrating because of superflous interfaces.

My rule of thumb: use an interface when you indeed have multiple different implementations behind, not just for the sake of it.


EDIT:

My "much harder" was probably exagerated. I should simply have written "harder" or "inconvinient".

To illustrate this, in eclipse. With a method from a class:

  • Ctrl+click jumps inside the method (1 click).

With a method from an interface, it would be:

  • Ctrl+click
  • move on top
  • right click on interace name
  • open type explorer
  • select class
  • open outline
  • go to method

...So, yeah, you can do it with good IDE support, but it's still quite annoying.

dagnelies
  • 5,493
1

Decoupling is not a good thing by itself. It is only a good thing where you need it, and bad everywhere else. If you are using an interface on a place where should be high cohesion, then you are doing it wrong.

Every interface has a cost and a value, if you are not considering it and blindly make them everywhere, then you are doing it simply wrong.

Meo
  • 111
  • 2
0

I program data business applications

Those generally doesn't suite for the classic "true" OOP where the object implements its own behaviour because of all business rules that depends of the current connected user and so on. Instead I have the well known anemic model + services, because I didn't find anything that suit more to my needs, except for some independant technicals components.

My services have always interfaces, because even if I have one implementation, there will be a time where I want to mock them. I abstract a little but not too much since at the time I do it, I don't know that much how much I could need and how to do it properly, and my time is better spĂȘnt that wasting time on that (YAGNI/KISS).

Of course since I start with only one implementation, my interface is pretty much a copy of whats my implementation need to expose but that's on purpose. If I need to have another implementation that will by nature force my to refactor my interface to a more generic one, and I will do it when it's need to be.

Note : I use regular name for the interface of my services I use (no "I" or whatever), because the consumer of those service really don't care they're interfaces as said @candied_orange

Walfrat
  • 3,536