I don't like interfaces
People seem to really like interfaces. Interfaces allow you to
- Create a list of methods so your API can use that instead of a class
- Write shared code in the interface
- Define an API, so if your codebase needs to switch to another vendor or implementation, it'd be easier since the API would be smaller
- Declare capabilities such as clonable, comparable, and more
- Have reflection work with mocks/DI/reflection libraries
All of which is stupid. The first and last are fucking stupid. I'll explain
Why I don't like interfaces
The biggest problem I have with interfaces is that it encourage people to write multiple implementations of everything. Do you know what happens when you have multiple implementations of something? They become inconsistent. I've seen code that cared so much about which implementation threw an exception that it was checking the text of an inner exception to tell them apart
Having multiple implementations is prone to bugs. Not just because there's more code (from having more implementations). There are plenty of people who would copy code from a known good implementation and not be around when it's discovered the original implementation didn't support a corner case (or scope has increased). People then need to hunt for why a bugfix didn't seem to fully work when it was a less common path running code that didn't get the fix
Another problem not mentioned in the list is that interfaces block the optimizer from doing good work. In one project I was on, switching everything to use my class directly instead of an interface caused benchmarks to run almost twice as fast (55% of wall clock); that's insane
What's stupid in the list?
In order
- Multiple implementations and the problems that come with it
- Not necessary when you have a single well-tested implementation
- It's rare to switch vendors, especially on something that doesn't have a standard around it. However, my issue around this is people hamstringing themselves by using a narrow API rather than taking advantage of what's supported in the technology (or vendor) you have chosen
- This isn't stupid; it's good that everything has consistent names for cloning, comparing, and others. It's just unnecessary to use an interface
- Mocks and DI are things I don't talk about and probably won't write about. Just say no
When I like interfaces
When I like interfaces, I really like them. I mostly like using them because virtual methods are being used across very different types. Not to swap implementation, but to have very different code run. I'll give an example
Let's say you're parsing text to create a tree, or rather, html since most people know what a DOM is. You'll see tags such as div, p, a, em, img, and more, many of which are recursive. You'll need a way to interact with all of them. Some things you'll need to do are
- Create a new instance
- Remove the element/instance (which also removes child nodes)
- Render (which includes child nodes)
- Size the element
- Query/Style it
There's almost no code overlap between a div that gets it size from its children, and an image which does not have children and defaults its size based on a file the page/server may not have seen yet. This is when I like interfaces: to apply operations across multiple types that have very little to do with eachother, controlled through data. Third parties get to control the data and get the UI/results they want. Some other systems using trees controlled by data are build systems (config is a tree), and compilers (ever heard of the abstract syntax tree?) To express operations, I can't think of a clearer way than interfaces.
Another place I like interfaces is IO. Plenty of my code has an input or output stream. I have no idea what the other side is connected to. An input stream might be text from a test file, a socket, an instance decompressing data in real time, a generator (like a fuzzer or /dev/zero), could be anything. This input or output stream is at the boundary of an API, and really none of my business what it's connected to. I care about the bytes I can get from or put into the stream, but after that it's usually up to the OS (network, disk, etc.) or external machine to handle the rest.
A third thing I'll mention but is rarely used, is an addon or plugin system. I generally don't like third party code in my process when I can help it, so most of the time I'll be using IO to communicate with these (as an example, language servers use serialization+IO, which falls under the second place I like interfaces.)