Когда вы разрабатываете API для библиотеки кодов, вы хотите, чтобы его было легко использовать хорошо, а трудно - плохо. В идеале вы хотите, чтобы это было доказательство идиота.
Возможно, вы также захотите сделать его совместимым со старыми системами, которые не могут обрабатывать дженерики, такими как .Net 1.1 и Java 1.4. Но вы не хотите, чтобы из-за более нового кода это было болезненно.
Мне интересно, как лучше сделать вещи легко повторяемыми в безопасном для типов виде ... Вспоминая, что вы не можете использовать дженерики, так что Java Iterable<T>
отсутствует, как и .Net * IEnumerable<T>
.
Вы хотите, чтобы люди могли использовать расширенный цикл for в Java (for Item i : items)
и цикл foreach
/ For Each
в .Net, и вы не хотите, чтобы им приходилось выполнять какое-либо приведение. По сути, вы хотите, чтобы ваш 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()
четко заявили, что он возвращает защитную копию. Но многие люди не будут читать эту конкретную часть документации.
Хотя я бы приветствовал предложения, мне кажется, что не существует идеального способа сделать его простым, понятным, безопасным для типов, и идиотом. Я потерпел неудачу, если меньшинство людей невольно клонировало массивы тысяч раз, или это приемлемая цена за простую, безопасную для типов итерацию для большинства ?
NB. Мне случается, что я проектирую эту библиотеку для Java и .Net, поэтому я попытался сделать этот вопрос применимым к обоим. И я пометил это как независимый от языка, потому что это проблема, которая может возникнуть и для других языков. Примеры кода на Java, но C # будет аналогичным (хотя и с возможностью сделать свойство доступа Appointments
).
ОБНОВЛЕНИЕ : Я провел несколько быстрых тестов производительности, чтобы увидеть, насколько это изменилось в Java. Я проверял:
- клонирование массива один раз и итерация по нему с использованием расширенного цикла for
- перебор ArrayList с использованием
расширенный цикл
- перебирает неизменяемое
ArrayList (от
Collections.unmodifyableList) используя
расширенный цикл
- итерация по массиву плохим способом (повторное клонирование при проверке длины
и при получении каждого проиндексированного элемента).
Для 10 объектов относительные скорости (многократные повторения и медиана) были такими:
- 1000
- 1300
- 1300
- 5000
Для 100 объектов:
- 1300
- 4900
- 6,300
- 85500
Для 1000 объектов:
- 6400
- 51700
- 56200
- 7000300
Для 10000 объектов:
- 68000
- 445000
- 651000
- 655180000
Грубые цифры, конечно, но достаточно, чтобы убедить меня в двух вещах: