返回具体或抽象数据类型?

时间:2009-05-25 06:57:32

标签: coding-style encapsulation

我正在阅读Code Complete,在本书的最后,在关于重构的章节中,作者列出了一些你应该做的事情,以便在重构时提高代码质量。

他的一点是总是作为特定类型的数据返回,特别是在返回集合,迭代器等时。所以,正如我所理解的那样,而不是返回,比如说,{ {1}},如果在方法中使用该数据类型,则应返回Collection<String>

这让我感到困惑,因为听起来他鼓励人们打破隐藏信息的规则。现在,我在谈论访问者时理解这一点,这是一个明确的案例。但是,在计算和修改数据时,方法的抽象级别意味着没有直接的数据结构,我发现最好将作为抽象的数据类型返回,只要数据不是分崩离析(例如,我不会返回HashSet<String>而不是Object

所以,我的问题是: Code Complete建议总是尽可能地返回特定的数据类型,并允许向下转换,而不是保持需要知道 - 基础,我只是不明白?

6 个答案:

答案 0 :(得分:3)

我认为大多数情况都是错误的。它一定要是: 尽可能宽松,具体视需要

在我看来,你应该总是返回List而不是LinkedList或ArrayList,因为差异更多的是实现细节而不是语义细节。来自Google collections api for Java的人更进一步:他们返回(并期望)迭代器,这就足够了。但是,他们还建议尽可能返回ImmutableList,-Set,-Map等,以显示他不必制作防御副本的来电者。

除此之外,我认为不同列表实现的性能不是大多数应用程序的瓶颈。

答案 1 :(得分:1)

找不到任何证据来证实我的说法,但这个想法/指引似乎是:

接受输入时尽可能宽容。选择特定类型的通用类型。这意味着客户可以使用不同专用类型的方法。因此,IEnumerable或IList作为输入参数意味着该方法可以运行ArrayList或ListItemCollection。它最大化了您的方法有用的机会。

返回值时尽可能严格。如果可能,请选择特殊类型。这意味着客户端不必进行二次猜测或跳过箍来处理返回值。专业类型也具有更强大的功能。如果您选择返回IList或IEnumerable,则调用者可以使用您的返回值执行的操作数量会大幅减少 - 例如如果在ArrayList上返回IList,要获取返回的元素数 - 使用Count属性,客户端必须向下转换。但是,这样的向下倾斜击败了目的 - 今天工作..明天不会(如果你改变返回对象的类型)。因此,出于所有目的,客户端无法轻易获得元素数量 - 导致他编写平凡的样板代码(在多个地方或作为辅助方法)

这里的摘要取决于上下文(大多数规则的例外情况)。例如。如果最有可能使用返回值是客户端将使用返回的列表来搜索某个元素,则返回支持某种搜索方法的List Implementation(类型)是有意义的。使客户端尽可能简单地使用返回值。

答案 2 :(得分:1)

大多数情况下,应该返回一个接口,或者可能是一个表示返回的返回值的抽象类型。如果要返回X列表,请使用List。如果需要返回列表类型,这最终会提供最大的灵活性。

也许以后你会意识到你想要返回一个链表或一个只读列表等。如果你把一个具体的类型卡住了,那就很难改变。使用该界面解决了这个问题。

@Gishu

如果您的api要求客户在大多数情况下直接投出您的设计。如果客户需要转换为Y,为什么还要返回X.

答案 3 :(得分:0)

我可以看到,在某些情况下,如何返回更具体的数据类型可能会有用。例如,知道返回值是LinkedList而不仅仅是List将允许您从列表中删除,因为它知道它将是有效的。

答案 4 :(得分:0)

我认为,在设计接口时,您应该设计一种方法来尽可能地返回抽象数据类型。返回特定类型将使该方法的目的更清楚它们返回的内容。

另外,我会这样理解:

尽可能以抽象的方式返回数据类型=尽可能返回特定的数据类型

即。当你的方法应该返回任何集合数据类型返回集合而不是对象。

告诉我,如果我错了。

答案 5 :(得分:0)

特定的返回类型更有价值,因为它:

  1. 通过投射或反射发现功能来减少可能的性能问题
  2. 提高了代码可读性
  3. 实际上并不会暴露超出必要的范围。
  4. 特别选择函数的返回类型以满足其所有调用者的需要。调用函数应该尽可能抽象地使用返回变量,因为调用函数知道如何使用数据。

    是否只需要遍历结构?是否有必要对结构进行分类?改造它?克隆吗?这些只是调用者可以回答的问题,因此可以使用抽象类型。被调用的函数必须提供所有这些情况。

    事实上,如果您现在拥有的最具体的用例是Iterable&lt;字符串&gt;,那很好。但通常情况下 - 您的调用者最终需要获得更多详细信息,因此从特定的返回类型开始 - 它不需要任何费用。