Not Everything Needs an Interface

I used to have an interface for nearly everything when building applications and I’ve been pulling back on that position lately. Here I’ll explain two cases where I’ve pulled back and written fewer interfaces.

No implements Keywords Does Not Mean There Are No Interfaces

This is important: every single object has an interface it presents to the world. That interface is how clients of the object interact with it. Public methods, public properties — all that is part of the interface. What I’m talking about here is the use of an explicit interface Foo { } and class Bar implements Foo { }.

Entities Probably Don’t Need Interfaces

My usual pattern was something like this:

interface Client
{
  public function getId() : string;
  public function getName() : string;
}

final class DefaultClient implements Client
{
  // ...
}

The Client is a header interface. This would be great if swapping out the implementation was necessary, but I’ve had only a few cases where that had to happen.

What about tests, though? In cases like this where the actual entity is closer to a record-like object with a bag of properties and no real behavior I’d favor just using the actual implementation in tests. Is that less isolated and perhaps less proper? Yes and probably.

Even in cases where the entity did have behavior removing the final and using the implementation as the basis for a test double works just fine.

class Client
{
  public function getId() : string 
  {
    // ...
  }
  
  // ...
}

Ignore This Advice When…

…there is an actual need for an interface that’s driven by the needs of the library or application. Multiple implementations of an entity based on a type field in the storage backend or something similar?

Like all things programming, using an interface for anything is a judgement call. Look for signals and use explicit interfaces where it makes sense.

One Off Services Don’t Need Interfaces

Similar to the entity thing above, I’d find myself writing a service interface with only one implementation.

interface Importer
{
  // `Import` probably does most of the work here, but the 
  // Importer might do things like notify other parts of the
  // system.
  public function start(Import $import); 
}

final class DefaultImporter implements Importer
{
  // ...
}

I love single method role interfaces (also known by a more obscure name: a function). In cases like this I found myself writing a role interface that only ever had one implementation. This one implementation interfaces tended to glue together only other domain objects and never talked outside the system. In cases like that, I don’t think an interface is necessary. This type of service object can often be replaced by a command bus.

Testing wise, I’ve found that services like described above tend to have very narrow uses. Creating a test double from the implementation itself can usually work.

class Importer
{
  public function start(Import $import)
  {
    // ...
  }
}

Ignore This Advice When..

As above, ignore this advice when design pressure indicates it should be ignored. Multiple implementations? Use an interface. Service touching the outside world? Definitely use an interface. Tests not working using doubles based on the implementation? Keep that interface.

This Stuff is Hard to Talk About

Mostly it’s hard because everything is a judgement call that’s unique to the context in which the code resides. I’ve written a lot of large applications in the past decade or so that I’ve had to live with and maintain since they first went live. These are two instances where I’ve found myself pulling back from explicit interface usage.