Click here to Skip to main content
14,271,874 members

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

Rate this:
3.28 (58 votes)
Please Sign up or sign in to vote.
3.28 (58 votes)
6 Aug 2019CPOL
Maybe not literally. But I will try to convince you to write fewer interfaces

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 Dog implements interface IAnimal, we are assured it (not always) can Move and Eat. Of course, a Dog can also Bark or 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:

namespace Calculator
{
    public interface IMathCalculator
    {
        int Sum(int a, int b);
        int Subtract(int a, int b);
    }
}

With implementation like this:

namespace Calculator
{
    public class MathCalculator : IMathCalculator
    {
        public int Sum(int a, int b)
        {
            return a + b;
         }

        public int Subtract(int a, int b)
        {
            return a - b;
        }
    }
}

Or even something like this:

public class CalculatorConfigProvider : ICalculatorConfigProvider
{
    public double GetPiValue()
    {
        return 3.14;
    }
}

public interface ICalculatorConfigProvider
{
    double GetPiValue();
}

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:

Kernel.Bind<ICalculatorConfigProvider>().To<CalculatorConfigProvider>();
Kernel.Bind<IMathCalculator>().To<MathCalculator>();
Kernel.Bind<ICalculatorResolver>().To<CalculatorResolver>();

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:

public class OrderGenerator
{
    private readonly TaxCalculator calculator;

    public OrderGenerator(TaxCalculator calculator)
    {
        this.calculator = calculator;
    }

    public Order Generate(IEnumerable<Product> products)
    {
        var order = new Order();
        foreach (var product in products)
        {
            var price = calculator.CalculatePrice(product);
            order.AddPosition(product, price);
        }

        return order;
    }
}

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

public class TaxCalculator
{
    public Price CalculatePrice(Product product)
    {
        // Some domain-specific calculations for getting price of a product.
    }
}

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 TaxCalculator and 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. :)

public interface ITaxCalculator
{
    public Price CalculatePrice(Product product);
}

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 0 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.

History

  • 7th August, 2019: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Manfredzik
Software Developer
Poland Poland
Hello from Poland Wink | ;)

I have been a .NET Developer since 2012. Since then I have had an opportunity to work with many projects written in technologies such as WinForms, WPF, ASP.NET MVC and ASP.NET WebAPI.
Ocasionally I did some projects in my spare time (for fun, on the studies project or as a remote part-time job).
But first of all I love coding and the possibility to increase my programming skills.

For few years I have been into client-side coding in JavaScript or TypeScript with different frameworks. I try to be up to date with latest trendings but actually the client side world is moving so fast I barerly can keep up with it Smile | :)

Cheers,
Miłosz

Comments and Discussions

 
QuestionAn example of why I need interfaces Pin
Fernando A. Gomez F.20-Aug-19 7:22
memberFernando A. Gomez F.20-Aug-19 7:22 
PraiseI couldn't agree more Pin
chaz stone16-Aug-19 5:03
memberchaz stone16-Aug-19 5:03 
Suggestionalternative to interfaces Pin
Mr.PoorEnglish13-Aug-19 16:49
memberMr.PoorEnglish13-Aug-19 16:49 
QuestionBasicly it always depends on the use case Pin
Ronald Hoek12-Aug-19 11:24
memberRonald Hoek12-Aug-19 11:24 
QuestionIoC container Pin
Member 1405679712-Aug-19 7:25
memberMember 1405679712-Aug-19 7:25 
QuestionMy Thoughts Pin
Adam David Hill12-Aug-19 4:43
professionalAdam David Hill12-Aug-19 4:43 
AnswerRe: My Thouhts Pin
Mr.PoorEnglish13-Aug-19 17:04
memberMr.PoorEnglish13-Aug-19 17:04 
GeneralRe: My Thoughts Pin
Adam David Hill14-Aug-19 7:40
professionalAdam David Hill14-Aug-19 7:40 
GeneralRe: My Thoughts Pin
Mr.PoorEnglish20-Aug-19 20:15
memberMr.PoorEnglish20-Aug-19 20:15 
QuestionAgree Pin
thelazydogsback9-Aug-19 7:23
memberthelazydogsback9-Aug-19 7:23 
QuestionHow to do the same with unity DI Pin
Mou_kol8-Aug-19 20:44
memberMou_kol8-Aug-19 20:44 
QuestionO Pin
Emil Steen8-Aug-19 10:46
memberEmil Steen8-Aug-19 10:46 
GeneralMy vote of 1 Pin
Member 30271208-Aug-19 10:17
memberMember 30271208-Aug-19 10:17 
QuestionRe: My vote of 1 Pin
Mr.PoorEnglish13-Aug-19 17:12
memberMr.PoorEnglish13-Aug-19 17:12 
QuestionGenerics, reflection and injection Pin
CFQüeb8-Aug-19 8:29
memberCFQüeb8-Aug-19 8:29 
GeneralI would go even further Pin
Paul Gehrman8-Aug-19 8:31
memberPaul Gehrman8-Aug-19 8:31 
SuggestionProvide Guidelines PinPopular
Davie212408-Aug-19 6:58
memberDavie212408-Aug-19 6:58 
QuestionI agree but... Pin
tatkosmurff8-Aug-19 2:39
membertatkosmurff8-Aug-19 2:39 
AnswerRe: I agree but... Pin
Dong Xie8-Aug-19 5:56
memberDong Xie8-Aug-19 5:56 
GeneralRe: I agree but... Pin
tatkosmurff8-Aug-19 10:19
membertatkosmurff8-Aug-19 10:19 
BugLeaking bucket ... Pin
Dong Xie8-Aug-19 1:50
memberDong Xie8-Aug-19 1:50 
GeneralRe: Leaking bucket ... Pin
netizenk8-Aug-19 5:47
professionalnetizenk8-Aug-19 5:47 
GeneralRe: Leaking bucket ... Pin
Dong Xie8-Aug-19 5:57
memberDong Xie8-Aug-19 5:57 
GeneralRe: Leaking bucket ... Pin
#realJSOP8-Aug-19 8:16
mve#realJSOP8-Aug-19 8:16 
GeneralMy vote of 3 Pin
BillWoodruff8-Aug-19 1:47
mveBillWoodruff8-Aug-19 1:47 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Article
Posted 6 Aug 2019

Tagged as

Stats

15.4K views
9 bookmarked