C# 为每个类提取接口是最佳实践吗?

C# 为每个类提取接口是最佳实践吗?,c#,interface,software-design,C#,Interface,Software Design,我见过这样的代码:每个类都有一个它实现的接口 有时它们都没有通用的接口 它们就在那里,它们被用来代替具体的物体 它们不为两个类提供通用接口,并且特定于类所解决问题的领域 有什么理由这样做吗?如果是原则的一部分。基本上,代码取决于接口,而不是实现 这允许您轻松地交换实现,而不会影响调用类。它允许更松散的耦合,使系统的维护更容易 随着系统的发展和变得越来越复杂,这个原则越来越有意义 接口定义了一种行为。如果实现一个或多个接口,则对象的行为与所描述的一个或多个接口类似。这允许类之间的松散耦合。当您必须

我见过这样的代码:每个类都有一个它实现的接口

有时它们都没有通用的接口

它们就在那里,它们被用来代替具体的物体

它们不为两个类提供通用接口,并且特定于类所解决问题的领域

有什么理由这样做吗?

如果是原则的一部分。基本上,代码取决于接口,而不是实现

这允许您轻松地交换实现,而不会影响调用类。它允许更松散的耦合,使系统的维护更容易


随着系统的发展和变得越来越复杂,这个原则越来越有意义

接口定义了一种行为。如果实现一个或多个接口,则对象的行为与所描述的一个或多个接口类似。这允许类之间的松散耦合。当您必须用另一个实现替换一个实现时,它非常有用。类之间的通信应始终使用接口完成,除非类之间确实紧密地绑定在一起。

如果您希望确保将来能够注入其他实现,则可能会有。对于某些(也许是大多数)情况,这是一种过度的杀伤力,但对于大多数习惯来说也是如此——如果你习惯了,你就不会有太多时间去做。而且,由于您永远无法确定将来要替换什么,因此在每个类上提取接口是有意义的


对于一个问题,从来都不是只有一个解决方案。因此,同一接口可能总是有多个实现。

这可能看起来很愚蠢,但这样做的潜在好处是,如果在某个时候你意识到有更好的方法来实现某个功能,你可以编写一个实现同一接口的新类,并更改一行,使所有代码都使用该类:分配接口变量的行

这样做(编写一个实现相同接口的新类)也意味着您可以始终在新旧实现之间来回切换以进行比较

结果可能是,您永远不会利用这种便利性,最终产品实际上只使用为每个接口编写的原始类。如果是这样,那太好了!但是编写这些接口并没有花费太多时间,如果您需要它们,它们会为您节省大量时间。

在重新审视这个答案后,我决定对其进行一些修改

不,为每个类提取接口不是最佳实践。这实际上可能适得其反。但是,接口之所以有用,有几个原因:

  • 测试支持(模拟、存根)
  • 实现抽象(进一步扩展到IoC/DI)
  • C#中的协方差和逆变支持等辅助功能
为了实现这些目标,接口被认为是良好的实践(并且实际上是最后一点所必需的)。根据项目的大小,您会发现您可能永远不需要与接口交谈,或者出于上述原因之一,您一直在提取接口

我们维护了一个大型应用程序,其中一些部分非常好,而另一些则缺乏关注。我们经常发现自己在进行重构,以将接口从类型中拉出来,使其可测试,或者我们可以更改实现,同时减少更改的影响。我们这样做也是为了减少“耦合”效应,如果您对公共API不严格,具体类型可能会意外施加这种效应(接口只能表示公共API,因此对我们来说,本质上变得非常严格)

这就是说,可以在没有接口的情况下抽象行为,也可以在不需要接口的情况下测试类型,因此它们不是上述要求。只是,在这些任务中支持您的大多数框架/库都可以有效地针对接口进行操作。
我将把我的旧答案留给上下文

接口定义公共契约。实现接口的人员必须实现此合同。消费者只看到公共合同。这意味着实现细节已从使用者抽象出来

目前,这种方法的一个直接用途是单元测试。接口很容易模仿,存根,伪造,随便你说

另一个直接的用途是依赖注入。为给定接口注册的具体类型提供给使用接口的类型。类型并不特别关心实现,因此它可以抽象地请求接口。这允许您在不影响大量代码的情况下更改实现(只要契约保持不变,影响范围就非常小)


对于非常小的项目,我倾向于不费心,对于中等项目,我倾向于在重要的核心项目上费心,对于大型项目,几乎每个类都有一个接口。这几乎总是为了支持测试,但在某些情况下,注入行为或行为抽象可以减少代码重复。

我不认为这对每个类都是合理的


问题在于您希望从哪种类型的组件中获得多少重用。当然,您必须计划更多的重用(无需稍后进行重大重构),而不是当前实际使用的重用,但是为程序中的每个类提取抽象接口将意味着您的类比需要的少

接口很好,因为您可以在(单元)测试时模拟类

我至少为所有涉及外部资源(如数据库、文件系统、Web服务)的类创建接口,然后编写一个模拟或使用模拟框架来模拟行为。

没有实践
class Coordinate
{
  public Coordinate( int x, int y);
  public int X { get; }
  public int y { get; }
}
class RoutePlanner
{
   // Return a new list of coordinates ordered to be the shortest route that
   // can be taken through all of the passed in coordinates.
   public List<Coordinate> GetShortestRoute( List<Coordinate> waypoints );
}
class RobotTank
{
   public RobotTank( IRoutePlanner );
   public void DriveRoute( List<Coordinate> points );
}