Oop 具体实现是否应该提供它实现的接口中不存在的任何公共API?

Oop 具体实现是否应该提供它实现的接口中不存在的任何公共API?,oop,interface,design-principles,dependency-inversion,Oop,Interface,Design Principles,Dependency Inversion,“代码到接口”被视为良好实践。这样的代码易于单元测试,并支持松散耦合。用户只知道接口,连接具体对象的责任在最高层(这可以在一些init代码中或在框架的帮助下完成) 我的问题是关于遵循代码到接口的实践:这是否意味着一个具体的类永远不能声明其接口中不存在的任何公共方法 否则,它将迫使用户依赖于具体的实现。这将使这种方法难以进行单元测试;如果测试失败,那么确定它是由于调用方代码中的问题还是由于具体方法而失败将需要额外的努力。这也将打破依赖倒置原则。这将导致类型检查和向下浇铸,这被认为是不好的做法 这是

“代码到接口”被视为良好实践。这样的代码易于单元测试,并支持松散耦合。用户只知道接口,连接具体对象的责任在最高层(这可以在一些init代码中或在框架的帮助下完成)

我的问题是关于遵循代码到接口的实践:这是否意味着一个具体的类永远不能声明其接口中不存在的任何公共方法


否则,它将迫使用户依赖于具体的实现。这将使这种方法难以进行单元测试;如果测试失败,那么确定它是由于调用方代码中的问题还是由于具体方法而失败将需要额外的努力。这也将打破依赖倒置原则。这将导致类型检查和向下浇铸,这被认为是不好的做法

这是完全可以接受的,前提是新方法对类的操作不是至关重要的,特别是当有人认为它是超类或接口时,它如何工作

ArrayList提供了很好的例子。它有一些方法可以让您管理它的内部内存,如或。如果您知道您正在使用ArrayList,并且需要更精确地分配内存,那么这些工具有时会很有用,但ArrayList的基本操作并不需要这些工具,特别是让它作为常规列表运行时,它们不是必需的

事实上,接口本身可以通过这种方式添加新方法。考虑,扩展集合。它添加了一系列依赖于集合元素顺序的方法(给我第一个、最后一个、从这里开始的子树,等等)。这些方法都没有在集合上定义,甚至元素是有序的这一事实也没有在集合契约中定义;但是Set方法在没有附加方法和顺序的情况下都可以正常工作


“编写接口代码”的建议是一个良好的开端,但它有点过于笼统。对该建议的改进是,“编码到您需要的最通用的接口。”如果您不需要ArrayList的方法(或者它的契约,比如它的随机访问性能),请列出代码;但如果你确实需要它们,那就一定要使用它们。

@yshavit的第三段说得对。实现“不够”基本接口的扩展,例如
公共接口NavigableSet extends SortedSet
(顺便说一句,
扩展集合扩展集合扩展Iterable

让我烦恼的是他的第二段。为什么要实现一些接口中未出现的API“非关键”方法?在
ArrayList
示例中,为什么不在接口中声明大小管理方法?也许是
ManagedSize
,它将描述
ArrayList
(和其他)类要实现的清晰行为,以及它实现的几个其他接口(我的JRE源代码说:
公共类ArrayList扩展了AbstractList implements List、RandomAccess、Cloneable、java.io.Serializable

使用这种方法,不需要决定哪些方法是“非关键的”,只会惊讶于某些客户端代码依赖于
ensureSize
之类的东西来帮助避免在时间关键阶段重新定位,或者
trimToSize
在算法上已知不需要进一步增长时,释放过度的超额分配。并不是说我在推广这种算法作为最佳实践,而是即使是非功能性的“行为管理”方法也应该在光中占有一席之地

最后,虽然我同意“知道线条在哪里,但颜色随你的喜好而定”的观点,但它并没有提供实际的指导。以下是对这方面的尝试:

  • 始终从编码到接口开始,即所有具体的公共方法都应在接口中声明:
  • 根据需要使用多个接口
  • 每个接口应将实现的API划分为连贯的非重叠方面,例如
    列表
    随机访问
    可克隆
    可序列化
  • 倾向于从更大范围的接口开始,并随着设计的发展(在编写ala瀑布之前,或随着代码的发展)将其分解;接口是更容易重构的设计构件之一
  • 如果您正在实现的给定接口“不足”:
  • 扩展基本接口并添加所需的方法,然后实现该接口,或者
  • 创建一个扩展接口(如上面的
    ManagedSize
    idea),只使用附加的方法,然后实现它们
  • 当你发现自己做不到这一点时,然后放松使事情正常运行所需的规则(通常,这将是一个实验性的试错“它正常吗?”循环)

  • #3“不能”的原因会有所不同,但我希望它们在应用程序设计之外,例如,我正在使用的ORM变得混乱,IDE插件没有正确地重构它,当一个类实现三个以上的接口时,我被迫使用的DSL转换器会失败…

    永远不要说不,但是,是的,这是一种不好的做法,因为你会遇到切片和铸造问题。有时这是必要的(例如,
    ArrayList.trimToSize
    )。@duffymo我喜欢“永不说永不”。。。有什么办法,或者说永远不要说永远不要做,因为我们有好的原则,比如接吻……没有办法。如果你必须,你必须。如果你能避免,你就不应该。没有魔法。我只是不喜欢这种教条。如果有一段时间你不得不这么做,那就知道后果吧。每个JDBC驱动程序开发人员,比如Oracle,都必须在接口中提供方法。但它们通常提供对它们来说是特殊的扩展。这是f