Do You Write Interfaces for Classes? I’m Begging You… Stop!

Nên sử dụng interface vì interface “CHÂT” lừ =/=

Introduction

There are a lot of guides on how to write good code. Many organizations implement static code analysis to verify and improve code quality. Developers become more and more conscious of what clean code is, SOLID, design patterns, GRASP… So many instructions how to code so we start to forget what this is all about.

Why We Need Interfaces?

The very first use case of an interface is to describe behavior of an object that implements it. If a class  Dogimplements interface  IAnimal, we are assured it (not always) can  Move and  Eat. Of course, a  Dog can also  Barkor  Sit, but this is specific implementation that differs from a  Bird that can  Fly.

A more complex use case of an interface is a polymorphism (often combined with dependency injection). If our code depends on abstraction (interfaces in this case), it is more flexible. We can use any class we want, that implements the necessary interface.

Thanks to interfaces, we can reduce coupling between classes. If your implementation is based on abstraction (such as interfaces), you could change the output of your application without changing the source code, just by changing implementation of the interface.

So Why Shouldn’t We Use Them?

At the beginning of my development journey, I didn’t ask many questions, I just did what other, more experienced colleagues did. But after some time, I started to see flaws in design and implementations.

How many times have you seen such an interface:

With implementation like this:

Or even something like this:

Ok, so what is possibly wrong in this code?

In my opinion, this interface is completely not necessary. Interface suggests possible multiple implementations. In this case, we have only one implementation with the exact name of the interface. The interface and its implementation is in the same namespace so it is not a problem to make use of a concrete implementation instead of an abstraction.

What is more, if you use IoC container (e.g., Ninject), you need to register all classes with their interfaces. This is another thing to remember and it may look like this:

The bigger a project is, the more entries are added when registering IoC Container. Initialization classes become bigger and harder to maintain. If you need to register dependencies in two different scopes (e.g., request scope for WebApi and Named scope for some asynchronous endless loop), you need to register your dependencies twice and it is a disaster.

Real Problem – Business Logic

We were talking about good practices, coupling, maintainability and so on. But I think the real problem is when you deal with your domain and business logic. This is a crucial part of the application – sometimes, a key point and the purpose of the application or even the whole company. This part contains some serious calculations, algorithms or laws.

If you add an interface to such class and then inject this interface, you have absolutely no control of how it would be used. Especially when you are making an API or a library used by another application.

A simple example of an  OrderGenerator that is executing domain logic of generating an  Order from  Products shows an idea:

TaxCalculator is injected into  OrderGenerator and it is making some calculations based on product type, tax prices and country laws.

With this implementation, you are certain that  OrderGenerator will calculate the price correctly for a given product.

Now consider that on a Code Review, some Senior-Expert suggested adding an interface to a  TaxCalculatorand injecting an  ITaxCalculator to the  OrderGenerator. Seems pretty easy and it sounds like a good idea. You think of Interface segregation from SOLID, you know that interfaces are good. The code looks good too. 🙂

Now,  OrderGenerator became open for modifications. What does it mean? It means that you can implement a new  TaxCalculator, e.g.,  NoTaxCalculator that implements  ITaxCalculator and returns   as a price.

The problem is that it is a crucial part of your application. Or your ecosystem. Or your Company.

And now, someone can break it. 🙂

But I Need Interfaces To…

Well… I have heard some justifications why it is necessary to add interfaces to classes. Here are some of them:

  1. Register classes in IoC Container to inject them into another classes
  2. Mock classes in Unit Tests
  3. Write clean code and reduce coupling between classes

But, is it true?

IoC Container should resolve a class when it contains a  public constructor with all parameters that can be resolved by IoC Container.

If you add an interface JUST for a test – don’t. Your production code should not be written just to satisfy tests. You can do whatever you want in a test project, but leave the production code. 🙂

Using interfaces instead of concrete implementations reduce coupling and that’s a fact. But registering interface in the IoC Container adds complexity and lowers maintainability so if you use it only once in the application, it equalizes pros and cons in my opinion.

So What Should I Do?

First of all, you should think about meeting all business requirements. 🙂 If you write the cleanest code of all time, but don’t satisfy your business, then the application is useless.

Interfaces have great value and they should be used in every application. Thanks to interfaces, you can use polymorphism, add many patterns such us Strategy, Factory, Command and so on. You can inverse dependencies (D in SOLID) so if your domain uses repositories or adapters, you can inject interfaces and the implementation should be in a separate layer.

But as everything, you should use them wisely. 🙂 Not all classes should have their own interfaces. This is not the only solution.

Credit of Manfredzik

Please follow and like us:

Leave a reply

Your email address will not be published.