Do we need interfaces for dependency injection?

asp.net Coreasp.net Core-Mvc.Net CoreCoreclr

asp.net Core Problem Overview


I have an ASP.NET Core application. The application has few helper classes that does some work. Each class has different signature method. I see lot of .net core examples online that create interface for each class and then register types with DI framework. For example

 public interface IStorage
 {
    Task Download(string file);
 }

 public class Storage
 {
    public Task Download(string file)
    {
    }
 }

 public interface IOcr
 {
     Task Process();
 }

 public class Ocr:IOcr
 {
    public Task Process()
    {

    }
 }

Basically for each interface there is only one class. Then i register these types with DI as

 services.AddScoped<IStorage, Storage>();
 services.AddScoped<IOcr,Ocr>();

But i can register type without having interfaces so interfaces here look redundant. eg

 services.AddScoped<Storage>();
 services.AddScoped<Ocr>();

So do i really need interfaces?

asp.net Core Solutions


Solution 1 - asp.net Core

No, you don't need interfaces for dependency injection. But dependency injection is much more useful with them!

As you noticed, you can register concrete types with the service collection and ASP.NET Core will inject them into your classes without problems. The benefit you get by injecting them over simply creating instances with new Storage() is service lifetime management (transient vs. scoped vs. singleton).

That's useful, but only part of the power of using DI. As @DavidG pointed out, the big reason why interfaces are so often paired with DI is because of testing. Making your consumer classes depend on interfaces (abstractions) instead of other concrete classes makes them much easier to test.

For example, you could create a MockStorage that implements IStorage for use during testing, and your consumer class shouldn't be able to tell the difference. Or, you can use a mocking framework to easily create a mocked IStorage on the fly. Doing the same thing with concrete classes is much harder. Interfaces make it easy to replace implementations without changing the abstraction.

Solution 2 - asp.net Core

Does it work? Yes. Should you do it? No.

Dependency Injection is a tool for the principle of Dependency Inversion : https://en.wikipedia.org/wiki/Dependency_inversion_principle

Or as it's described in SOLID

> one should “depend upon abstractions, [not] concretions."

You can just inject concrete classes all over the place and it will work. But it's not what DI was designed to achieve.

Solution 3 - asp.net Core

I won't try to cover what others have already mentioned, using interfaces with DI will often be the best option. But it's worth mentioning that using object inheritance at times may provide another useful option. So for example:

public class Storage
 {
    public virtual Task Download(string file)
    {
    }
 }


public class DiskStorage: Storage
 {
    public override Task Download(string file)
    {
    }
 }

and registering it like so:

services.AddScoped<Storage, DiskStorage>();

Solution 4 - asp.net Core

No, we don't need interfaces. In addition to injecting classes or interfaces you can also inject delegates. It's comparable to injecting an interface with one method.

Example:

public delegate int DoMathFunction(int value1, int value2);

public class DependsOnMathFunction
{
    private readonly DoMathFunction _doMath;

    public DependsOnAFunction(DoMathFunction doMath)
    {
        _doMath = doMath;
    }

    public int DoSomethingWithNumbers(int number1, int number2)
    {
        return _doMath(number1, number2);
    }
}

You could do it without declaring a delegate, just injecting a Func<Something, Whatever> and that will also work. I'd lean toward the delegate because it's easier to set up DI. You might have two delegates with the same signature that serve unrelated purposes.

One benefit to this is that it steers the code toward interface segregation. Someone might be tempted to add a method to an interface (and its implementation) because it's already getting injected somewhere so it's convenient.

That means

  • The interface and implementation gain responsibility they possibly shouldn't have just because it's convenient for someone in the moment.
  • The class that depends on the interface can also grow in its responsibility but it's harder to identify because the number of its dependencies hasn't grown.
  • Other classes end up depending on the bloated, less-segregated interface.

I've seen cases where a single dependency eventually grows into what should really be two or three entirely separate classes, all because it was convenient to add to an existing interface and class instead of injecting something new. That in turn helped some classes on their way to becoming 2,500 lines long.

You can't prevent someone doing what they shouldn't. You can't stop someone from just making a class depend on 10 different delegates. But it can set a pattern that guides future growth in the right direction and provides some resistance to growing interfaces and classes out control.

(This doesn't mean don't use interfaces. It means that you have options.)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionLP13View Question on Stackoverflow
Solution 1 - asp.net CoreNate BarbettiniView Answer on Stackoverflow
Solution 2 - asp.net CoreMindingDataView Answer on Stackoverflow
Solution 3 - asp.net CoreRonCView Answer on Stackoverflow
Solution 4 - asp.net CoreScott HannenView Answer on Stackoverflow