为每个类提取一个接口是否是最佳实践?

我已经看到了代码,每个类都有一个它实现的接口。

有时候,他们都没有共同的界面。

他们只是在那里,而不是具体的物体。

他们不提供两个类的通用接口,而是针对类解决的问题的领域。

有什么理由要这样做吗?

重温这个答案后,我决定稍微修改一下。

不,最好的做法是为每个class级提取界面。 这实际上可能会适得其反。 但是,接口是有用的有几个原因:

  • testing支持(嘲笑,存根)。
  • 实施抽象(进一步到IoC / DI)。
  • 辅助的东西,如在C#中的co-和contra-variance支持。

为了实现这些目标,接口被认为是良好的实践(实际上是最后一点要求的)。 根据项目大小的不同,您会发现您可能永远都不需要与界面进行交谈,或者出于上述原因之一而不断提取界面。

我们保持了很大的应用,其中有些部分很好,有些则缺乏关注。 我们经常发现自己重构了一个types的接口,使其成为可testing的,所以我们可以改变实现,同时减less这种变化的影响。 如果您对公共API(接口只能代表一个公共API,因此本质上变得相当严格),那么我们也会这样做以减less具体types可能意外施加的“耦合”效应。

也就是说,可以抽象出没有接口的行为,也可以在不需要接口的情况下对types进行testing,所以它们不是对上述的要求 。 只是你可能用来支持你的这些任务的大多数框架/库都可以有效地对接口进行操作。


我将离开我的旧的答案的上下文。

接口定义了一个公共契约。 实现接口的人必须执行这个合同。 消费者只能看到公共合同。 这意味着实现细节已经从消费者中抽象出来了。

这些日子的直接使用是unit testing 。 接口很容易模拟,存根,假,你的名字。

另一个直接的用途是dependency injection 。 给定接口的注册具体types被提供给消费接口的types。 types并不关心实现,所以它可以抽象地询问接口。 这使您可以在不影响大量代码的情况下更改实现(只要合同保持不变,影响范围非常小)。

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

没有。

接口对于具有复杂行为的类非常有用,如果您希望能够创build该接口的模拟或伪类实现类以便在unit testing中使用,那么该接口就非常方便。

但是,一些类没有太多的行为,可以更像对待值,通常由一组数据字段组成。 为这样的类创build接口没有什么意义,因为这样做会导致不必要的开销,因为在模拟或提供接口的替代实现方面没有多大意义。 例如,考虑一个类:

class Coordinate { public Coordinate( int x, int y); public int X { get; } public int y { get; } } 

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

但是,class上

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

你可能会想为IRoutePlanner提供一个IRoutePlanner接口,因为有很多不同的algorithm可以用来规划路由。

另外,如果你有第三课:

 class RobotTank { public RobotTank( IRoutePlanner ); public void DriveRoute( List<Coordinate> points ); } 

通过给RoutePlanner一个接口,你可以为RobotTank编写一个testing方法,创build一个带有模拟RoutePlanner的testing方法,它只是返回一个坐标列表,而没有特定的顺序。 这将允许testing方法检查坦克在坐标之间正确导航而不testing路线计划者。 这意味着你可以编写一个只testing一个单元(坦克)的testing,而不用testing路由规划器。

不过,你会发现,将实际坐标提供给像这样的testing是很容易的,而不需要将它们隐藏在ICoordinate接口之后。

为您的项目中的每个类提取接口没有实际的原因。 这将是一个过度杀手。 他们之所以必须提取接口,是因为他们似乎实施了一个OOAD原则“ 接口程序而不是实现 ”。 你可以在这里find一个关于这个原理的更多信息。

具有接口和编码到接口使得更换实现变得更容易。 这也适用于unit testing。 如果你正在testing一些使用接口的代码,你可以(理论上)使用一个模拟对象而不是一个具体的对象。 这可以让你的testing更加专注和细化。

从我所看到的在实际生产代码中转换为testing(模拟)实现的情况更为常见。 是的,这是为了unit testing而发怒。

让我引用OO大师马丁·福勒(Martin Fowler)的话来给这个主题中最常见的答案增加一些坚实的理由。

这个摘录来自“企业应用架构模式” (参加“编程经典”和/或“每个开发人员必读”书籍类别)。

[Pattern] 分离的界面

(……)

何时使用它

当需要中断系统的两个部分之间的依赖关系时,可以使用分离的接口。

(……)

我遇到了许多开发人员,他们为每个课程都有单独的界面。 我认为这是过度的,特别是对于应用程序开发。 保持单独的接口和实现是额外的工作,特别是因为你经常需要工厂类(有接口和实现)。 对于应用程序,我build议仅在需要中断依赖关系时才使用单独的接口,或者您希望有多个独立的实现。 如果将接口和实现放在一起,之后需要将接口和实现分开,那么这是一个简单的重构过程,可能会被延迟,直到需要这样做。

回答你的问题:不

我已经看到了一些这种types的“花哨”的代码,开发者认为他是SOLID,而不是难以理解,难以扩展和太复杂。

这看起来可能很愚蠢,但是这样做的潜在好处是,如果在某个时候您意识到有一个更好的方法来实现某个function,您可以编写一个实现相同接口的新类,并将一行更改为使所有的代码都使用这个类:接口variables被分配的那一行。

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

这可能会导致你永远不能利用这个便利,而你的最终产品确实只是使用为每个接口编写的原始类。 如果是这样的话,太棒了! 但是写这些接口真的不需要太多时间,如果你需要它们,它们会为你节省很多时间。

我认为每个class级都不合理。

这是一个重要的问题,你期望什么types的组件。 当然,你必须规划更多的重用(以后不需要进行重大的重构),但是为了在程序中为每个类提取一个抽象接口,意味着你的类比需要。

我喜欢在时间或空间上以两种不同方式实现的接口,即在将来可以以不同的方式实现,或者在代码的不同部分有两个不同的代码客户机,这些客户机可能需要不同的实现。

你的代码的原作者可能只是机器人编码,或者他们正在聪明地准备版本韧性,或者为unit testing做准备。 更有可能的是前者,因为版本弹性是一个不常见的需求 – (即,客户端部署在哪里,无法更改,部署的组件必须与现有客户端兼容)

我喜欢那些依赖的接口,这些接口是值得与我打算testing的其他代码隔离的。 如果这些接口不是为了支持unit testing而创build的,那么我不确定他们是个好主意。 接口有维护成本,当需要使对象与另一个对象交换时,你可能想要一个接口只适用于几个方法(所以更多的类可以实现该接口),使用抽象类(以便默认行为可以在inheritance树中实现)。

所以预先需要的接口可能不是一个好主意。

可能有,如果你想确保将来能够注入其他的实现。 对于一些(也许是大多数)的案例来说,这太过于矫枉过正,但是和大多数习惯一样 – 如果你习惯了这种习惯,那么你就不会花很多时间去做。 而且既然你永远不能确定你将来想要replace什么 ,所以在每个类上提取一个接口确实有一点意义。

一个问题永远只有一个解决scheme。 因此,总是可以有多个同一接口的实现。

这些接口是很好的,因为你可以在(单元)testing时模拟类。

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

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

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

随着你的系统变得越来越复杂,这个原则越来越有意义!

接口定义了一个行为。 如果你实现了一个或多个接口,那么你的对象的行为就像这个接口描述的那样。 这允许类之间的松散耦合。 当你必须用另一个实现来replace实现时,它是非常有用的。 类之间的通信应该始终使用接口来完成,除非这些类真的是紧密相连的。

你为什么需要接口? 实际深思。 接口不是真的附加到类,而是附加到服务。 接口的目标是你允许别人用你的代码做的事情,而不用为他们提供代码。 所以它涉及到服务和pipe理。

再见