java - 没有泛型的愚蠢证明迭代的API设计

标签 java .net api language-agnostic api-design

在为代码库设计API时,您希望它易于使用且不会被不良使用。理想情况下,您希望它是白痴证明。

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

我想知道以某种类型安全的方式使事情易于迭代的最佳方法...请记住,您不能使用泛型,因此Java的Iterable<T>和.Net的IEnumerable<T>都不可用。

您希望人们能够使用Java (for Item i : items)中的增强型for循环以及.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()而不是appointments(),这可能会减少任何人以错误的方式使用它的可能性。但是,这也将使人们在仅想遍历约会时就更难找到。

当然,您会清楚地记录appointments(),说它返回了防御性副本。但是很多人不会读那些特别的文档。

尽管我欢迎提出建议,但在我看来,没有完美的方法可以使它变得简单,清晰,类型安全和白痴证明。如果少数人不知道数千次克隆数组,或者对于大多数人来说,为简单的类型安全的迭代支付的价格是否合理,我是否会失败?

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

更新:我进行了一些快速的性能测试,以了解这在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

  • 粗略的数字可以肯定,但足以使我相信两件事:
  • 克隆,那么绝对是迭代
    不是性能问题。事实上
    始终比使用
    清单。 (这是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 - 没有泛型的愚蠢证明迭代的API设计,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6360562/

    相关文章:

    java - 我如何使用 Retrofit 2.3.0 解析 json 对象

    .NET Regex - 匹配后获取字符串

    c# - 访问 Office 2003 文件

    .net - 国际奥委会最佳实践: How to best manage dependency graph?

    c# - 在 C# 中将 JSON 数组反序列化为对象

    ios - 移动应用程序的 openweathermap API 问题

    java - OpenCV:如何将 Mat 的每个值乘以指定的常数?

    java - 使用不同的查询参数 JOIN 不同的方法 : REST

    java - 手电筒无法打开

    api - 如何使用 Dart http 库、flutter 实现 Post API 调用