当用 Java 编程时,出于习惯,我几乎总是这样写:

public List<String> foo() {
    return new ArrayList<String>();
}

大多数时候甚至连想都没想。现在,问题是:我是不是该 总是 指定接口作为返回类型?或者是否建议使用接口的实际实现?如果是,在什么情况下?

显然,使用该界面有很多优点(这就是它存在的原因)。在大多数情况下,库函数使用什么具体实现并不重要。但也许在某些情况下它确实很重要。例如,如果我知道我将主要随机访问列表中的数据, LinkedList 会很糟糕。但如果我的库函数只返回接口,我根本不知道。为了安全起见,我什至可能需要将列表显式复制到 ArrayList:

List bar = foo();
List myList = bar instanceof LinkedList ? new ArrayList(bar) : bar;

但这看起来太可怕了,我的同事可能会在自助餐厅用私刑处死我。这是理所当然的。

你们有什么感想?您的指导方针是什么?您什么时候倾向于抽象解决方案?您什么时候会透露实现细节以获得潜在的性能提升?

有帮助吗?

解决方案

  

举例来说,如果我知道,我会   主要访问列表中的数据   随机,一个LinkedList会很糟糕。   但如果我的库函数只   返回界面,我根本就没有   知道。为了安全起见我可能   甚至需要明确地复制列表   到一个ArrayList。

至于其他人已经提到的,你只是要怎么在意图书馆已经实现的功能,以减少耦合和库的增加可维护性。

如果你作为一个库客户端,可以证明,实现您的使用情况差强人意,然后你可以联系负责人及有关的最佳途径讨要遵循(一种新的方法,这种情况下,或只是改变实现)。

这就是说,你的例子恶臭过早优化的。

如果该方法是或可能是关键的,它可能在文档中提到的实施细节。

其他提示

返回适当的接口来隐藏实现细节。您的客户应该只关心你的对象提供了,你不是如何实现它。如果您有私人ArrayList的开始,后来又对其他的东西(例如,LinkedLisk,跳跃列表等)是比较合适的,你可以改变实现,而不会影响客户如果返回的接口决定。那一刻你回到一个具体类型的机会丢失。

在面向对象编程中,我们希望封装尽可能多的数据。尽可能隐藏实际实现,尽可能抽象类型。

在这种情况下,我会回答 只返回有意义的内容. 。返回值作为具体类是否有意义?在你的例子中,问问自己:有人会在 foo 的返回值上使用 LinkedList 特定的方法吗?

  • 如果没有,就使用更高级别的接口。它更加灵活,并且允许您更改后端
  • 如果是的话,问问自己:我不能重构我的代码以返回更高级别的接口吗?:)

您的代码越抽象,更改后端时需要做的更改就越少。就这么简单。

另一方面,如果您最终将返回值强制转换为具体类,那么这是一个强烈的信号,表明您可能应该返回具体类。您的用户/队友不必了解或多或少的隐性合同:如果您需要使用具体方法,为了清楚起见,只需返回具体类即可。

简而言之:代码 抽象的, , 但 明确地 :)

而不能与CS报价里姆斯证明它(我是自学的),我一直用的口头禅了“接受至少获得,返回最衍生,”设计类的时候,它已经站我好多年了。

我想在界面与混凝土回报方面也就意味着,如果你正在试图减少依赖性和/或去耦合,返回接口通常是更为有用。但是,如果具体的类实现的更多的比接口,它通常是你的方法来获得具体类背部的来电者更有用的(即“最派生”),而不是aribtrarily他们限制传回了对象的功能的子集 - 除非你真正的需要的限制他们。再说,你也可以只增加了接口的覆盖范围。不用限制,这样我比较类轻率密封;你永远都不会知道。只是为了讨论一下该曼陀罗(其他读者)的前半部分,接受至少还衍生给出了你的方法的调用者的最大灵活性。

-Oisin

在一般情况下,对于一个面向公众的接口,诸如API的返回接口(如List)在具体实现(如ArrayList)效果会更好。

在使用ArrayListLinkedList的是,应考虑该库中最常见的情况下使用该库的实现细节。当然,在内部,有private方法换手LinkedLists未必是一件坏事,如果它提供的设施,这将使处理更容易。

有任何理由,一个具体的类不应该在实现中使用,除非有充分的理由相信,其他一些List类将在以后使用。但是,有一次,改变的实现细节不应作为面向公众的部分是精心设计的是只要痛苦。

在库本身应是一个黑箱它的消费者,所以他们并不真正担心内部发生了什么上。这也意味着,图书馆应被设计为使得它被设计成在其意图的方式被使用。

作为一个规则,我只传递回内部实现,如果我在一个库中的一些私人,内部工作,即使如此仅微。这才是公众和可能从我的模块我用接口的外部调用的,一切都还有工厂模式。

以这样的方式使用接口已被证明是编写可重用代码的一个非常可靠的方式。

主要的问题已经被回答你应该总是使用的接口。然而,我只是想提出意见

  

显而易见的是,使用所述接口具有许多优点(这就是为什么它的存在)。在大多数情况下,它并没有真正的问题是,库函数使用了什么具体的实现。但是,也许存在这样的情况的确很重要。举例来说,如果我知道,我将主要访问数据列表中的随机,一个LinkedList是坏的。但如果我的库函数只返回界面,我根本不知道。为了安全起见我甚至可能需要显式地在复制列表到一个ArrayList

如果你正在返回你知道具有差的随机存取性能的数据结构 - O(n)和通常的数据的LOT - 有其他接口应该使任何人都可以使用指定的代替列表中,像可迭代库将完全认识到,只有顺序访问是可用的。

选择正确类型返回不仅仅是接口与具体实现,它也是关于选择合适的接口。

不要紧那么多的API方法是否返回一个接口或一个具体的类;尽管每个人都在这里说,一旦代码被写入你几乎永远不变的implementiation类。

什么是更重要的:总是使用最小范围接口,你的方法的参数的!这样一来,客户有最大的自由度,可以使用类代码甚至不知道的。

当一个API方法返回ArrayList,我对此完全没有疑虑,但是当它需要一个ArrayList(或所有常见,Vector)参数,我认为追捕程序员和伤害他,因为这意味着我不能使用Arrays.asList()Collections.singletonList()Collections.EMPTY_LIST

抱歉不同意,但我认为基本规则如下:

  • 为了 输入 参数使用最多 通用的.
  • 为了 输出 价值观,最 具体的.

因此,在这种情况下,您希望将实现声明为:

public ArrayList<String> foo() {
  return new ArrayList<String>();
}

基本原理:输入案例是大家都知道并解释的:使用界面,就这样。然而,输出情况可能看起来违反直觉。您想要返回实现,因为您希望客户端拥有有关接收内容的最多信息。在这种情况下, 更多的知识就是更多的力量.

示例1:客户端想要获取第 5 个元素:

  • 返回集合:必须迭代直到第 5 个元素 vs 返回列表:
  • 返回列表: list.get(4)

示例2:客户端想要删除第五个元素:

  • 返回列表:必须创建一个没有指定元素的新列表(list.remove() 是可选的)。
  • 返回数组列表: arrayList.remove(4)

因此,使用接口很棒是一个大事实,因为它可以提高可重用性、减少耦合、提高可维护性并让人们感到高兴......但仅当用作 输入.

因此,该规则可以再次表述为:

  • 对你提供的东西要灵活。
  • 提供丰富的信息。

所以,下次,请返回实现。

您使用的接口抽象出来从实际执行。接口基本上是只为你的实现可以做的蓝图。

接口是好的设计,因为它们允许你改变实现细节,而不必担心任何消费者都直接影响,只要你现在也还你的界面说它做什么。

要使用的接口工作,你会实例化它们是这样的:

IParser parser = new Parser();

现在IParser将是你的界面,并且解析器将是你实现。现在,当你从上面的分析器对象工作,你将开始处理接口(IParser),这反过来将工作对你的实现(解析器)。

这意味着,你可以改变分析器的内部工作原理,只要你想尽可能多的,它永远不会影响到对你的IParser解析器接口运行的代码。

一般来说,如果不需要具体类的功能,则在所有情况下都使用接口。请注意,对于列表,Java 添加了 随机访问 标记类主要用于区分算法可能需要知道 get(i) 是否为常数时间的常见情况。

对于代码的使用,上面的 Michael 是对的,方法参数尽可能通用通常更为重要。在测试这种方法时尤其如此。

您会找到(或发现),当你返回接口,他们通过您的代码渗透。例如从方法A返回一个接口和你有无然后到一个接口传递到方式B

你在做什么是编程通过合同,虽然以有限的方式。

这给了你巨大的范围,以改变在底层实现(提供这些新对象履行现有的合同/预期的行为)。

由于这一切,你有利益在选择你的执行方面,以及如何替换行为(包括测试 - 用嘲讽,例如)。如果你还没有猜到了,我都赞成这一点,并尽量减少(或引进)接口尽可能。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top