Do I need interfaces for DI?

I have a main asp.net application. The application has several helper classes that do some work. Each class has a different signature method. I see many examples of the .net kernel on the Internet that create an interface for each class and then register types with a DI framework. for instance

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() { } } 

In principle, there is only one class for each interface. Then I register these types with DI as

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

But I can register the type without interfaces, so the interfaces look redundant. eg,

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

So do I really need interfaces?

+5
source share
4 answers

No, you donโ€™t need any interfaces for dependency injection. But you must use them!

As you pointed out, you can register specific types with a collection of services, and ASP.NET Core simply inserts them into your classes. The only advantage you get by simply creating instances with new Storage() is the service lifecycle management (transitional versus cloud or single).

This is only part of the power of using DI. As @DavidG noted, the big reason why interfaces interface so often with DI is due to testing. Creating classes depends on interfaces (abstractions), and not on other specific classes, which greatly facilitates their testing.

For your tests, you can create a MockStorage that implements IStorage , and your consumer class will not be able to distinguish it. Or you can use a fake framework to easily create a mock IStorage . The mocking framework either does not support faking specific classes, or it is very inconvenient. Interfaces are much simpler and cleaner.

+8
source

It works? Yes. Should you do this? No.

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

Or as described in SOLID

should "depend on abstractions, nodules."

You can just enter specific classes all over the place and it will work. But this is not what DD was designed for.

+8
source

I will not try to highlight what others have already mentioned; using interfaces with DI will often be the best option. But it's worth mentioning that using object inheritance can sometimes provide another useful option. For example:

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

and register it like this:

 services.AddScoped<Storage, DiskStorage>(); 
0
source

No, you do not need interfaces. In addition to injecting classes or interfaces, you can also introduce delegates. This is comparable to introducing an interface with a single 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 can do this without declaring a delegate, simply by entering Func<Something, Whatever> , and it will work as well. I tend to be delegate because it is easier to configure DI. Perhaps you have two delegates with the same signature that serve unrelated purposes.

One of the advantages of this is that it manages the code to segregate the interface. Someone may be tempted to add a method to the interface (and its implementation), because it is already injected somewhere so that it is convenient.

It means

  • The interface and implementation takes on responsibility that they probably should not have just because it is convenient for someone at the moment.
  • A class that depends on the interface can also grow in its responsibility, but it is more difficult to identify, since the number of its dependencies has not grown.
  • Other classes end up depending on a bloated, less divided interface.

I have seen cases where one dependency eventually turns into something that should really be two or three completely separate classes, because it was convenient to add to the existing interface and class instead of introducing something new. This, in turn, helped some classes get in the way of 2500 lines.

You cannot stop someone from doing what they do not need. You cannot prevent someone from simply making the class depend on 10 different delegates. But he can set a pattern that will help future growth in the right direction and provide some resistance to growing interfaces and classes.

(This does not mean that you are not using interfaces. It means that you have options.)

0
source

Source: https://habr.com/ru/post/1266042/


All Articles