Click here to Skip to main content
15,115,261 members
Articles / Programming Languages / C#
Article
Posted 6 Aug 2019

Tagged as

Stats

27.6K views
11 bookmarked

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

Rate me:
Please Sign up or sign in to vote.
3.38/5 (58 votes)
6 Aug 2019CPOL5 min read
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:

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

With implementation like this:

C#
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:

C#
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:

C#
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:

C#
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.

C#
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. :)

C#
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 8:22
MemberFernando A. Gomez F.20-Aug-19 8:22 
AnswerRe: An example of why I need interfaces Pin
Manfredzik5-Jul-21 12:28
MemberManfredzik5-Jul-21 12:28 
PraiseI couldn't agree more Pin
chaz stone16-Aug-19 6:03
Memberchaz stone16-Aug-19 6:03 
Suggestionalternative to interfaces Pin
Mr.PoorEnglish13-Aug-19 17:49
MemberMr.PoorEnglish13-Aug-19 17:49 
QuestionBasicly it always depends on the use case Pin
Ronald Hoek12-Aug-19 12:24
MemberRonald Hoek12-Aug-19 12:24 
QuestionIoC container Pin
Member 1405679712-Aug-19 8:25
MemberMember 1405679712-Aug-19 8:25 
AnswerRe: IoC container Pin
Mike Barthold17-Sep-19 4:26
professionalMike Barthold17-Sep-19 4:26 
QuestionMy Thoughts Pin
Adam David Hill12-Aug-19 5:43
professionalAdam David Hill12-Aug-19 5:43 
For me the prime benefits of writing to interfaces are:

1. It forces me to write/structure my code better. In the same way that TDD impacts the way you design your classes, interfaces fundamentally force you to consider the design of the class from the outside, and its relationship to the outside world.

2. Having loose, decoupled interface-driven code means it's easy to change. This is a fundamental part of the topic I don't see any reference to in your article. The point isn't necessarily that you already have multiple implementations from the word go. Give it a few months and you might have multiple for unseen reasons. A class can be entirely replaced without changing a single line of code elsewhere, provided the interface is maintained. If the interface changes, then you become inherently aware of the scope of the change.


A couple of asides:

Quote:
What is more, if you use IoC container (e.g., Ninject), you need to register all classes with their interfaces.
Nope. Well, I can't speak for Ninject, but plenty of IoC have options for Auto-Wiring[^].


Quote:
If you add an interface to such class and then inject this interface, you have absolutely no control of how it would be used.
How is the amount of control any different than injecting the concrete class? You have either the same or more control with interfaces. The consumer of an interface can only interact with the declared members. With a concrete class they might surprise you and interact with a public element you weren't expecting.


Hope these ideas are helpful.

modified 14-Aug-19 13:19pm.

AnswerRe: My Thouhts Pin
Mr.PoorEnglish13-Aug-19 18:04
MemberMr.PoorEnglish13-Aug-19 18:04 
GeneralRe: My Thoughts Pin
Adam David Hill14-Aug-19 8:40
professionalAdam David Hill14-Aug-19 8:40 
GeneralRe: My Thoughts Pin
Mr.PoorEnglish20-Aug-19 21:15
MemberMr.PoorEnglish20-Aug-19 21:15 
QuestionAgree Pin
thelazydogsback9-Aug-19 8:23
Memberthelazydogsback9-Aug-19 8:23 
QuestionHow to do the same with unity DI Pin
Mou_kol8-Aug-19 21:44
MemberMou_kol8-Aug-19 21:44 
QuestionO Pin
Emil Steen8-Aug-19 11:46
MemberEmil Steen8-Aug-19 11:46 
GeneralMy vote of 1 Pin
Member 30271208-Aug-19 11:17
MemberMember 30271208-Aug-19 11:17 
QuestionRe: My vote of 1 Pin
Mr.PoorEnglish13-Aug-19 18:12
MemberMr.PoorEnglish13-Aug-19 18:12 
AnswerRe: My vote of 1 Pin
Member 30271201-Sep-19 13:38
MemberMember 30271201-Sep-19 13:38 
GeneralIt's a c# - article, not c++ Pin
Mr.PoorEnglish4-Sep-19 11:00
MemberMr.PoorEnglish4-Sep-19 11:00 
GeneralRe: It's a c# - article, not c++ Pin
Member 30271204-Sep-19 15:15
MemberMember 30271204-Sep-19 15:15 
GeneralRe: It's a c# - article, not c++ Pin
Mr.PoorEnglish7-Sep-19 4:23
MemberMr.PoorEnglish7-Sep-19 4:23 
GeneralRe: It's a c# - article, not c++ Pin
Member 30271208-Sep-19 6:22
MemberMember 30271208-Sep-19 6:22 
Generalcan anybody help us out? Pin
Mr.PoorEnglish8-Sep-19 9:23
MemberMr.PoorEnglish8-Sep-19 9:23 
GeneralRe: can anybody help us out? Pin
Member 30271208-Sep-19 16:23
MemberMember 30271208-Sep-19 16:23 
GeneralRe: can anybody help us out? Pin
Mr.PoorEnglish16-Sep-19 9:27
MemberMr.PoorEnglish16-Sep-19 9:27 
GeneralRe: can anybody help us out? Pin
Manfredzik5-Jul-21 12:46
MemberManfredzik5-Jul-21 12:46 

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.