为什么使用接口而不是DI的抽象类?

Cha*_*ams 8 c# dependency-injection

我开始学习依赖注入的过程,并且我看到为什么使用DI是一个好主意的原因之一是它明确指定了您的依赖关系,这也使您的代码更加清晰.

我也注意到接口被大量使用,但我想知道为什么我们不会仅仅为了指定默认构造函数而使用抽象类?

当然,抽象类中不能包含任何实现.

不会这样:

abstract class FooBase
{
    protected IBar _bar;

    FooBase(IBar bar)
    {
        _bar = bar;
    }

    abstract void DoSomething();
    abstract void DoSomethingElse();
}
Run Code Online (Sandbox Code Playgroud)

更清楚地展示FooBase对象的依赖性超过:

interface IFoo
{
    IBar Bar { get; }

    void DoSomething();
    void DoSomethingElse();
}
Run Code Online (Sandbox Code Playgroud)

请记住,我是这个概念的新手,所以要好:)

Ale*_*kov 5

一个技术原因 - 在没有多重继承的语言中强制使用特定父类(Java/C#)将极大地限制"接口"的实现自由.

请注意,"接口"字后面隐藏着两个概念,这使得在C#中更难理解:

  • "接口"和抽象概念:与对象交互的定义良好的属性/方法集; 与一个对象合作的合同.
  • "interface"作为特定语言的类型(C#/ Java) - 给定语言中契约的一种可能表示.

抽象/具体类可用于表示契约,但强制限制C#中的契约实现者.

其他一些语言(如C++)没有这样的限制,抽象类是很好的选择.其他语言(即"duck-types"JavaScript)没有这样的类/接口区别,但您仍然可以与对象"契约".

样品:

为了提供更多应该在C#中实现此限制的上下文:DI通常与某种形式的TDD一起使用,或者至少与基本单元测试一起使用.因此,您尝试编写一些使用DI的抽象基类的代码和测试.

abstract class Duck { 
  abstract void Quack();
}
class ConcreteDuck : Duck {...}
Run Code Online (Sandbox Code Playgroud)

现在,如果你决定编写测试,你可能已经有了可以帮助你模拟对象的测试类(如果你没有使用现有的一次)

class MockBase {...}

class MockDuck : MockBase,?????? // Can't use Duck and MockBase together...
Run Code Online (Sandbox Code Playgroud)


Mar*_*tus 5

接口定义了合同.Abstract基类定义了一个行为.

从本质上讲,您可以提供一个实现多个接口的类,然后可以将其注入多个类,但是您只有一个抽象基类(至少在C#中).

考虑在容器上注册类型(最好是组合根)并考虑解决依赖关系的点(构造函数或属性).

这个SO将涵盖接口与基类的更多方面