编程语言
首页 > 编程语言> > Java – 没有泛型的Idiot-Proof迭代的API设计

Java – 没有泛型的Idiot-Proof迭代的API设计

作者:互联网

当您为代码库设计API时,您希望它易于使用,并且难以使用.理想情况下,你希望它是白痴证明.

您可能还希望使其与无法处理泛型的旧系统兼容,例如.Net 1.1和Java 1.4.但是你不希望从较新的代码中使用它会很麻烦.

我想知道以一种类型安全的方式使事物易于迭代的最佳方法…记住你不能使用泛型,所以Java的Iterable< T>就像.Net的IEnumerable< T>一样.

您希望人们能够在Java中使用增强的for循环(对于Item i:items),以及.Net中的foreach / For Each循环,并且您不希望他们必须进行任何转换.基本上,您希望您的API现在既友好又向后兼容.

我能想到的最好的类型安全选项是数组.它们完全向后兼容,并且易于以类型安全的方式进行迭代.但是数组并不理想,因为你不能让它们变成不可变的.因此,当您拥有一个包含数组的不可变对象时,您希望人们能够迭代,为了保持不变性,您必须在每次访问它时提供防御性副本.

在Java中,做(MyObject [])myInternalArray.clone();超快.我确信.Net中的等价物也非常快.如果你喜欢:

class Schedule {
   private Appointment[] internalArray;
   public Appointment[] appointments() {
       return (Appointment[]) internalArray.clone();
   }
}

人们可以这样做:

for (Appointment a : schedule.appointments()) {
    a.doSomething();
}

它将简单,清晰,类型安全,快速.

但他们可以这样做:

for (int i = 0; i < schedule.appointments().length; i++) {
    Appointment a = schedule.appointments()[i];
}

然后它会非常低效,因为整个约会数组将在每次迭代时克隆两次(一次用于长度测试,一次用于获取索引处的对象).如果阵列很小,这不是一个问题,但如果阵列中有数千个项目,则非常可怕.育.

有人会这样做吗?我不确定……我猜这在很大程度上是我的问题.

你可以调用方法toAppointmentArray()而不是约会(),这可能会降低任何人以错误的方式使用它的可能性.但它也会让人们更难以找到他们只想迭代约会的时候.

当然,你会清楚地记录约会(),说它会返回一个防御性的副本.但很多人不会阅读那些特定的文档.

虽然我欢迎提出建议,但在我看来,没有完美的方法可以使它变得简单,清晰,类型安全,而且是白痴证明.如果少数人不知道克隆阵列数千次,或者对于大多数人来说,为简单的,类型安全的迭代付出的代价是否可以接受?

NB我碰巧正在为Java和.Net设计这个库,这就是为什么我试图让这个问题适用于两者.我将其标记为与语言无关,因为这也是其他语言可能出现的问题.代码示例使用Java,但C#类似(尽管可以选择使约会访问者成为属性).

更新:我做了一些快速性能测试,看看它在Java中有多大差异.我测试过:

>克隆一次数组,并使用增强的for循环迭代它
>使用迭代迭代ArrayList
增强的for循环
>迭代不可修改的
ArrayList(来自
Collections.unmodifyableList)使用
增强的for循环
>以错误的方式遍历数组(在长度检查中重复克隆它)
并在获取每个索引项目时).

对于10个对象,相对速度(多次重复并取中位数)如下:

> 1,000
> 1,300
> 1,300
> 5,000

对于100个对象:

> 1,300
> 4,900
> 6,300
> 85,500

对于1000个对象:

> 6,400
> 51,700
> 56,200
> 7,000,300

对于10000个对象:

> 68,000
> 445,000
> 651,000
> 655,180,000

粗略的数字肯定,但足以说服我两件事:

>克隆,然后迭代肯定
不是性能问题.事实上
它一直比使用a更快
名单. (这是why Java’s
enum.values() method returns a
defensive copy of an array instead of
an immutable list
.)
>如果您反复调用该方法,
不必要地反复克隆阵列,
对于所讨论的阵列来说,性能变得越来越大.这太可怕了.没有惊喜.

解决方法:

clone()很快但不是我所说的超级快.

如果你不相信人们有效地编写循环,我不会让他们写一个循环(这也避免了对clone()的需要)

interface AppointmentHandler {
    public void onAppointment(Appointment appointment);
}

class Schedule {
    public void forEachAppointment(AppointmentHandler ah) {
        for(Appointment a: internalArray)
            ah.onAppointment(a);
    }
}

标签:java,net,language-agnostic,api,api-design
来源: https://codeday.me/bug/20190726/1542694.html