避免与Java Collection接口

时间:2015-05-13 22:33:02

标签: java algorithm oop design-patterns

这些天我正在阅读Code Complete书,我已经通过了关于耦合级别(简单数据参数,简单对象,对象参数和语义耦合)的部分。语义是“最差”的类型:

  

当一个模块发生最阴险的耦合时   不使用其他模块的某些语法元素,而是使用某些语义   了解另一个模块的内部工作原理。

本书中的示例通常会导致运行时失败,并且是典型的错误代码,但今天我遇到的情况是我真的不确定如何对待。

首先我有一个课程,让我们称之为Provider获取一些数据并返回List Foo个。

class Provider
{
   public List<Foo> getFoos()
   {
      ArrayList<Foo> foos = createFoos();//some processing here where I create the objects
      return foos;
   }
}

消费类执行算法,处理Foo,根据某些属性从List合并或删除,这并不重要。该算法使用列表的头部进行所有处理。因此,有许多操作读取/删除/添加到头部。

(我刚刚意识到我可以让算法看起来像合并排序,在数组的一半上递归调用它,但这不能回答我的问题:))

我注意到我正在返回ArrayList,因此我更改了提供类“getFoos方法”以返回LinkedListArrayList具有头部删除的O(n)复杂度,而LinkedList具有恒定的复杂性。但它让我感到震惊,我可能会产生语义依赖。代码肯定适用于List的两种实现,没有副作用,但性能也会降低。我写了这两个类,所以我很容易理解,但如果一个同事必须实现该算法,或者如果他使用Provider作为另一个有利于随机访问的算法的源,那该怎么办呢。如果他不打扰内部,就像他不应该这样,我会搞砸他的算法性能。

声明返回LinkedList的方法也是可能的,但是“接口程序”原则呢?

有没有办法处理这种情况,或者我的设计在根源上有缺陷?

3 个答案:

答案 0 :(得分:4)

一般问题是,生产者如何以消费者喜欢的形式返回某些东西?通常,消费者需要在请求中包含首选项。例如

  1. 作为旗帜 - getFoos(randomOrLinked)
  2. 不同的方法 - getFoosAsArrayList(), getFoosAsLinkedList()
  3. 传递一个创建所需列表的函数 - getFoos(ArrayList::new)
  4. 或传递所需的输出列表 - getFoos(new ArrayList())
  5. 但是,制片人可能有权说,这对我来说太复杂了,我不在乎。我将返回一个适合大多数用例的表单,并且消费者需要正确处理它。如果我认为ArrayList是最好的,我就会这样做。 (实际上,您可能有更好的选择 - 环形结构 - 适合两种用例)

    当然,它应该有详细记录。或者您可以诚实地返回ArrayList作为方法签名,只要您承诺它。不要过于担心&#34;界面&#34; - ArrayList是一个接口(在一般意义上),Iterable是一个接口,所以两者之间的List接口有什么特别之处。

    您的设计可能会有另一个批评 - 您返回一个可变数据结构,以便消费者可以直接修改。这比返回只读数据结构更不可取。如果可以,您应该返回基础数据的只读视图;视图的构造应该是便宜的。如果需要可变的消费者,消费者需要自己制作副本。

答案 1 :(得分:3)

你需要在某个地方做出妥协。在这种情况下,有两个对我有意义的妥协:

  1. 如果您不知道Provider的所有消费者都将执行适合LinkedList的操作,请坚持使用返回类型为List的签名返回ArrayList的实现(ArrayList是一个很好的全面List实现)。然后在调用方法中,将返回的List包装在LinkedList中:LinkedList<Foo> fooList = new LinkedList<>(provider.getFoos());让调用者负责自己的优化。
  2. 如果您知道Provider的所有消费者将以LinkedList - 适当的方式使用它,那么只需将返回类型更改为LinkedList - 或添加其他方法返回LinkedList
  3. 我非常喜欢前者,但两者都有道理。

答案 2 :(得分:2)

您可以让来电者创建LinkedList,而不是提供商 - 即更改

List<Foo> foos = provider.getFoos();

List<Foo> foos = new LinkedList<>(provider.getFoos());

然后,提供商返回的列表类型并不重要。缺点是仍然会创建ArrayList,因此在效率和清洁度之间存在权衡。