我们需要依赖注入的接口吗?

时间:2017-03-28 20:25:26

标签: asp.net-core asp.net-core-mvc .net-core coreclr

我有一个ASP.NET核心应用程序。该应用程序有很少的辅助类可以完成一些工作。每个类都有不同的签名方法。我在网上看到很多.net核心示例,它们为每个类创建接口,然后使用DI框架注册类型。例如

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

    }
 }

基本上每个界面只有一个类。然后我用DI注册这些类型

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

但我可以在没有接口的情况下注册类型,因此这里的接口看起来多余。例如

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

我真的需要接口吗?

4 个答案:

答案 0 :(得分:16)

不,您不需要需要接口来进行依赖注入。但你应该使用它们!

正如您所注意到的,您可以使用服务集合注册具体类型,ASP.NET Core会将它们注入您的类中。简单地使用new Storage()创建实例的唯一好处是service lifetime management(瞬态与范围与单身)。

但这只是使用DI的力量的一部分。正如@DavidG指出的那样,接口经常与DI配对的重要原因是测试。使您的类依赖于接口(抽象)而不是其他具体类使它们更容易测试。

对于您的测试,您可以创建一个MockStorage来实现IStorage,而您的消费者类不应该能够分辨出来。或者,您可以使用模拟框架轻松创建模拟IStorage。用具体的类做同样的事情要困难得多。模拟框架要么不支持伪造具体类,要么非常尴尬。接口更简单,更清晰。

答案 1 :(得分:9)

有用吗?是。你应该这样做吗?没有。

依赖注入是依赖性倒置原则的工具:https://en.wikipedia.org/wiki/Dependency_inversion_principle

或者正如SOLID

中所描述的那样
  

应该“取决于抽象,[不]具体结果。&#34;

可以只是在整个地方注入具体的课程,它会起作用。但它不是DI旨在实现的目标。

答案 2 :(得分:2)

我不会试图涵盖其他人已经提到的内容,使用DI接口通常是最好的选择。但值得一提的是,有时使用对象继承可能会提供另一种有用的选择。例如:

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


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

并像这样注册:

services.AddScoped<Storage, DiskStorage>();

答案 3 :(得分:0)

不,你不需要接口。除了注入类或接口,您还可以注入委托。它与使用一种方法注入接口相当。

示例:

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);
    }
}

您可以在不声明委托的情况下执行此操作,只需注入Func<Something, Whatever>即可。我倾向于代表,因为设置DI更容易。您可能有两个具有相同签名的代理,这些代理服务于不相关的目的。

这样做的一个好处是它将代码引向接口隔离。有人可能会试图在接口(及其实现)中添加一个方法,因为它已经被注入某个地方,所以它很方便。

这意味着

  • 界面和实施有责任,他们可能不应该只是因为它对当下的人来说很方便。
  • 依赖于界面的类也可以增加其责任,但由于其依赖关系的数量没有增长,因此难以识别。
  • 其他类最终取决于膨胀,较少隔离的界面。

我已经看到过单个依赖关系最终会变成真正应该是两个或三个完全独立的类的情况,所有这些都是因为添加到现有的接口和类而不是注入新的东西很方便。这反过来帮助了一些班级成长为2,500行。

你不能阻止别人做他们不应该做的事情。你不能阻止某人只依靠10个不同的代表来上课。但它可以设定一种模式,指导未来在正确方向上的增长,并为不断增长的接口和类控制提供一些阻力。

(这并不意味着不使用接口。这意味着你有选择权。)