2

This question already has an answer here:

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.

marked as duplicate by gnat, TheCatWhisperer, Deduplicator, David Arno, Becuzz Jun 7 '18 at 12:42

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

  • 4
    The main danger of creating an interface from a single class is that you will get it horribly wrong. Imagine if an ancient Australian defined the interface for IMammal. They'd all have a required sizeOfPouch() method... – user949300 Jun 7 '18 at 0:12
  • 6
    Are you confusing "interface" and "concretion" in software design terms with "interface" and "class" in Java/C#? You can program to an interface without actually writing a separate Java interface. – user253751 Jun 7 '18 at 0:53
  • 1
    I think "abstraction" has several and different meanings in software engineering. To me, interfaces have nothing to do with abstractions. Abstractions have nothing to do with implementation details. It has to do with design. Conceptualization. Something that you could achieve with or without interfaces. – Laiv Jun 7 '18 at 6:28
  • 2
    Classes, in the types of languages you imply, are both abstractions and implementations of that abstraction. Classes provide an “interface” (API). The interface keyword enables you to specify a class with no implementation, to work around language limitations. From the user’s perspective, neither a class nor an interface is any better or more abstract than the other. Interfaces may make construction more difficult. – Frank Hileman Jun 7 '18 at 12:07
  • 1
    @froodley Correct, but a class in such languages is both an abstraction (the operations provided) and an implementation in one textual document. I.e. whereas in other languages they might be separate, such languages allow you to combine them into one unit. They can still be separated... a class can leave members unimplemented; if all are so, it is equivalent to an interface, without the multiple inheritance advantages. – Frank Hileman Jun 8 '18 at 0:22
6

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.

  • 2
    In Java, your DriverControls interface would commonly be named Drivable. – Jan Van den bosch Jun 7 '18 at 9:04
  • Basically for the reason you gave, I am. I don't want to deal in some specific thing called a Car that does things some one specific way. To me that is just one-off, trash code. I want to deal in interactions with a contract. I know what I'm getting knows about .drive() and .steer(), and returns me a string, and I don't need to know whether it's a car or a boat. – froodley Jun 7 '18 at 17:03
  • 1
    @froodley Agreed. Which means the real debate should be about the trouble that can be caused when you use concrete classes first and later refactor them to interfaces. It's hard to do this and respect the open closed principle. Worse is how easily it is for awareness of Cars concrete status to spread. I explored trying to head that off while staying concrete here. It's not pretty. – candied_orange Jun 7 '18 at 18:46
3

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?"

  • 1
    Creating good abstractions is hard because they remove complexity. Bad abstractions instead add to it. – Deduplicator Jun 7 '18 at 12:07
  • Creating great abstractions is hard, creating abstractions that serve the purpose of decoupling is not that hard. If my consumers can replace my module or class with another module or class that can serve up the same signature, I feel I've done the job correctly and they are not tightly coupled to what I've done. – froodley Jun 7 '18 at 23:29
  • 3
    @froodley If your interface and class have the same signature, you haven't decoupled anything, other than construction. – Frank Hileman Jun 8 '18 at 23:10
3

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.

  • 1
    Any IDE worth its salt knows how to jump to implementations. This is in no way a valid point unless you're using poor tools. – Kayaman Jun 7 '18 at 10:26
  • @Kayaman Not if you use poor abstractions, try to find that one specific implementation by its Runnable interface. – Meo Jun 7 '18 at 11:27
  • 1
    @Meo we're not talking about Runnable here, that's a faulty made up example similar to "what if you want to find a specific subclass of Object". There's no problems in finding a specific implementation of MyInterface. Unless of course you've decided to have all your custom classes implement MyInterface for no reason, but then there are bigger problems involved. – Kayaman Jun 7 '18 at 11:32
  • 1
    Hi ho! Take it easy guys! ;) My "much harder" was probably exagerated. I should simply have written "harder" or "inconvinient". To illustrate this, with a class method in eclipse, Ctrl+click jumps inside the method (1 click). With 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 IDE support, but it's still 7 clicks or so, which is still quite annoying. – dagnelies Jun 7 '18 at 13:40
  • 1
    @froodley: IMHO, adding interfaces systematically is just adding useless noise. It doesn't improve decoupling, nor does it ensure that the underlying implementation respects the contract. It just adds unnecessary indirections, noise and annoys seasonned developers. There are a lot of cases where interfaces are useful, but applying the systematically is IMHO just stupid. – dagnelies Jun 8 '18 at 12:09
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.

  • That's your take, mine is quite the opposite. High cohesion conceptually within a module is still achieved when you deal with contracts that are closely related. I am of the opinion that building based on contracts all the time removes any ambiguity and enforces the right kind of thinking, while resulting in a highly-swappable codebase "for cheap" compared to trying to decouple later, when many consumers already depend on your concretions. But again, I'm looking for some references to reading material on the subject. – froodley Jun 7 '18 at 23:26
  • 1
    @froodley From the user's point of view, the only dependency difference between what you call a "concrete" class and an "interface" is the constructor dependency. You can construct a class that provides a constructor. You can't construct specialized classes that provide no constructor, such as an interface. An interface and a class are identical from the users point of view, otherwise. – Frank Hileman Jun 8 '18 at 0:27
  • An interface is a contract. A class is a machine that produces some implementation of that contract. These are very, very different. I am not convinced at all that dealing in concrete machines is the same as dealing in contracts. – froodley Jun 8 '18 at 12:54
  • @froodley A contract is a set of operations and semantics. An interface is simply a set of operations, a set of signatures, that looks exactly the same as a class, from the user's perspective. As far as I can tell, what you call "concrete" is the presence of a constructor in a class. Decoupling would mean that you can change the API independently of the implementation. A class can do this, an interface can do this, but real decoupling means using several interfaces or a dynamic language. – Frank Hileman Jun 8 '18 at 23:14
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

  • In Java you could mock without interfaces exactly the same as with them (using Mockito and such), unless you would want to implement the class manually of course. – Meo Jun 7 '18 at 12:26

Not the answer you're looking for? Browse other questions tagged or ask your own question.