Разработка API для функций, работающих с массивами - PullRequest
7 голосов
/ 13 января 2012

Я разрабатываю API на Java для набора численных алгоритмов, которые воздействуют на массивы двойных чисел (для финансовой статистики в реальном времени, как это происходит).По соображениям производительности API должен работать с примитивными массивами, поэтому List<Double> и тому подобное не вариант.

Типичным вариантом использования может быть объект алгоритма, который принимает два входных массива и возвращает выходной массивкоторый содержит результат, вычисленный из двух входных данных.

Я хотел бы установить согласованные соглашения о том, как параметры массива используются в API, в частности:

  • Должен ли я включитьсмещения со всеми функциями, чтобы пользователи могли работать с частями большего массива, например someFunction(double[] input, int inputOffset, int length)
  • Если функции требуются как входные, так и выходные параметры, должен ли ввод или вывод идти первым в списке параметров?
  • Должен ли вызывающий объект выделить выходной массив и передать его в качестве параметра (который потенциально может быть использован повторно), или функция должна создавать и возвращать выходной массив при каждом его вызове?

Задачами являются достижение баланса эффективности, простоты для пользователей API и согласованности как внутри API, так и с установленнымиусловные обозначения.

Очевидно, что вариантов много, так каков лучший общий дизайн API?

Ответы [ 6 ]

3 голосов
/ 13 января 2012

Так что это действительно звучит как три вопроса, так что вот мое мнение.

Конечно, это очень субъективно - поэтому - ваш пробег может варьироваться:

  1. Да.Всегда включайте смещение и длину.Если большинству вариантов использования для конкретной функции эти параметры не нужны, перегрузите функцию, чтобы ввод и длина не требовались.

  2. Для этого я бы следовал стандарту, используемому arraycopy:

    arraycopy (Object src, int srcPos, Object dest, int destPos, int length)

  3. Разница в производительности здесь будет незначительной, если вызывающий повторновызывает ваши служебные функции.Если это всего лишь одна вещь, не должно быть никакой разницы.Если они вызываются неоднократно, вы должны сделать так, чтобы вызывающая сторона отправила вам выделенный массив.

2 голосов
/ 13 января 2012

Предполагая, что вы работаете с массивами, достаточно маленькими для размещения в стеке или в Eden, распределение происходит очень быстро.Поэтому нет ничего плохого в том, чтобы функции выделяли свои собственные массивы для возврата результатов.Это большая победа для читабельности.

Я бы предложил начать с того, чтобы ваши функции работали с целыми массивами, и ввел опцию для вызова функции с помощью только части массива, только если вы обнаружили, что онаполезно.

2 голосов
/ 13 января 2012
  1. Если вы это сделаете, укажите опцию по умолчанию (начинается с 0, полная длина).
  2. Я думаю, что большинство пользователей ожидают выхода 2-го. Однако, если вы можете использовать varargs, это может изменить ваше мнение.
  3. Мне нравится, что вызывающая сторона передает выходной массив, но с параметром для нуля, что означает, что метод будет выделять.

Разрабатывая комментарий vararg, предположим, у вас есть метод для добавления двух массивов. Если в качестве 1-го аргумента положить выходной массив arg, а в конце - 2 входных массива, тривиально расширить метод для добавления N массивов.

Разработка на # 3, позволяющая вызывающим абонентам передавать в выходной массив, иногда это более эффективно. И даже если выигрыш незначителен, ваши пользователи, имеющие дело с примитивными массивами, вероятно, пришли из C или FORTRAN фона и думают, что выигрыш будет большим и будет жаловаться, если вы не позволите им быть «эффективными». : -)

1 голос
/ 13 января 2012

Я думаю, что разработка API в значительной степени субъективна и / или должна сильно зависеть от «сценариев использования» API. С другой стороны, варианты использования вашего API полностью зависят от клиентского кода.

Сказав все это лично, я бы воспользовался перегрузкой метода и пошел бы к следующей структуре:

Метод со всеми параметрами:

void someFunction(int[] input1, int[] input2, int offset, int length, int[] output)

Это основная функция. Все остальные функции просто вызывают это с соответствующими параметрами.

int[] someFunction(int[] input1, int[] input2, int offset, int length)

Вызывает первую функцию, но выделяет и возвращает выходной массив от имени вызывающей стороны.

void someFunction(int[] input1, int[] input2, int[] output)

int[] someFunction(int[] input1, int[] input2)

Обратите внимание, что общая стратегия состоит в том, чтобы сократить список параметров, исключив «необязательные» параметры.

В целом, я стараюсь избегать изменения поведения метода в зависимости от того, является ли параметр (например, выходной массив) null. Это может затруднить выявление тонких ошибок таким образом. Поэтому я предпочитаю два разных стиля вызова: один, в котором указывается (и требуется) выходной параметр, и другой, где метод возвращает свой вывод.

1 голос
/ 13 января 2012

Главное в дизайне API, демонстрирующем множество функций, это его внутренняя согласованность.Все остальное приходит как отдаленная секунда.

Решение о том, передавать или нет пары индекс / длина, зависит от способа использования API.Если вы ожидаете, что пользователи напишут серию вызовов методов, которые принимают или помещают данные в разные сегменты одного и того же массива, как в System.arrayCopy, то вам нужны пары индекс / длина.В противном случае, это излишнее.

Ввод первым или вывод первым - ваше решение, но как только вы сделаете это, придерживайтесь его во всех методах с похожими сигнатурами.

Передача выходного буфера является разумнойопция, только если буфер повторно используется в клиенте.В противном случае это пустая трата усилий на создание и поддержку дополнительного набора методов API.Конечно, это решение тесно связано с вашим выбором пары индекс / длина: если вы берете индекс и длину, вы также должны использовать выходной буфер.

0 голосов
/ 13 января 2012

Я бы использовал List<Double> и чтобы методы возвращали вывод как новый List:

public List<Double> someFunction(List<Double> input)
...