为每个类提取接口是最佳做法吗?

the*_*row 48 c# interface software-design

我见过代码,其中每个类都有一个它实现的接口.

有时它们并没有共同的界面.

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

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

有什么理由这样做吗?

Sco*_*ham 42

没有.

接口适用于具有复杂行为的类,如果您希望能够创建该接口的模拟或伪实现类以用于单元测试,则特别方便.

但是,有些类没有很多行为,可以更像是值,通常由一组数据字段组成.为这样的类创建接口没有什么意义,因为这样做会在模拟或提供接口的替代实现时没有什么意义,会带来不必要的开销.例如,考虑一个类:

class Coordinate
{
  public Coordinate( int x, int y);
  public int X { get; }
  public int y { get; }
}
Run Code Online (Sandbox Code Playgroud)

你不太可能想要一个接口ICoordinate来使用这个类,因为除了简单地获取和设置XY值之外,以任何其他方式实现它没什么意义.

但是,班级

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 );
}
Run Code Online (Sandbox Code Playgroud)

您可能需要一个IRoutePlanner接口,RoutePlanner因为有许多不同的算法可用于规划路线.

另外,如果你有第三堂课:

class RobotTank
{
   public RobotTank( IRoutePlanner );
   public void DriveRoute( List<Coordinate> points );
}
Run Code Online (Sandbox Code Playgroud)

通过提供RoutePlanner一个接口,你可以编写一个测试方法,RobotTank用一个模拟创建一个RoutePlanner只返回一个没有特定顺序的坐标列表.这将允许测试方法检查罐在坐标之间正确导航而不测试路线规划器.这意味着您可以编写仅测试一个单元(油箱)的测试,而无需测试路线规划器.

你会看到,将真正的坐标输入到这样的测试中非常容易,而不需要将它们隐藏在ICoordinate界面后面.

  • 为Coordinate类示例+1.DTO的接口大部分时间都不是一个好主意. (2认同)

Ada*_*rth 28

重新回答这个问题之后,我决定稍微修改一下.

不,为每个类提取接口不是最佳实践.这实际上可能适得其反.但是,接口很有用,原因如下:

  • 测试支持(模拟,存根).
  • 实现抽象(进一步到IoC/DI).
  • 辅助事物,如C#中的co-contra和contra-variance支持.

为了实现这些目标,接口被认为是良好的实践(并且最后一点实际上是必需的).根据项目大小,您会发现您可能永远不需要与接口通信,或者由于上述原因之一而不断提取接口.

我们维持一个大型应用程序,其中一些部分很好,一些部分正在遭受缺乏关注.我们经常发现自己重构将接口拉出类型以使其可测试,或者我们可以更改实现,同时减少该更改的影响.我们还这样做是为了减少具体类型可能会在您的公共API不严格的情况下意外强加的"耦合"效应(接口只能代表公共API,因此对我们来说固有地变得非常严格).

也就是说,可以在没有接口的情况下抽象行为,并且可以在不需要接口的情况下测试类型,因此它们不是上述要求.只是您可以用来支持这些任务的大多数框架/库将有效地对抗接口.


我将留下我的旧答案的背景.

接口定义了公共合同.实现接口的人必须实现此合同.消费者只看到公共合同.这意味着实现细节已经从消费者中抽象出来.

这些天的直接用途是单元测试.接口很容易模拟,存根,假,你的名字.

另一个直接用途是依赖注入.给定接口的注册具体类型被提供给消费接口的类型.该类型并不特别关注实现,因此它可以抽象地询问接口.这允许您在不影响大量代码的情况下更改实现(只要合同保持不变,影响区域就非常小).

对于非常小的项目,我倾向于不打扰,对于中型项目,我倾向于打扰重要的核心项目,而对于大型项目,几乎每个类都有一个界面.这几乎总是支持测试,但在某些情况下注入行为或行为抽象以减少代码重复.

  • 类**的公共(非单独)接口定义了一个契约. (2认同)

and*_*fox 9

让我引用OO大师Martin Fowler为这个帖子中最常见的答案添加一些可靠的理由.

此摘录来自"企业应用程序架构模式"(参与"编程经典"和"或每个开发必读"书籍类别).

[Pattern] 分离界面

(......)

何时使用它

当您需要破坏系统的两个部分之间的依赖关系时,可以使用分离的接口.

(......)

我遇到过许多开发人员,他们为每个编写的课程都有单独的接口.我认为这是过度的,特别是对于应用程序开发.保持单独的接口和实现是额外的工作,尤其是因为您经常需要工厂类(具有接口和实现).对于应用程序,我建议仅在您想要破坏依赖关系或希望拥有多个独立实现时才使用单独的接口.如果将接口和实现放在一起并需要稍后将它们分开,这是一个简单的重构,可以延迟直到你需要这样做.

回答你的问题:没有

我自己看过这种类型的"花哨"代码,开发人员认为他是SOLID,但却难以理解,难以扩展且过于复杂.

  • 我已经看到一些开发人员说为每个行为类都有一个接口是过度和糟糕的设计——但仍然没有看到他们中的任何人解释如何以其他方式进行单元测试。 (2认同)

thi*_*eek 5

为项目中的每个类提取接口背后没有实际原因.这是一个过度杀戮.它们必须提取接口的原因是它们似乎实现了OOAD原则" 程序到接口,而不是实现 ".您可以在此处找到有关此原理的更多信息.