пятница, 27 декабря 2013 г.

Важность описания интерфейсов функций

Сегодня, прочитав статью http://habrahabr.ru/post/207390/, удивился тому факту, что автор не сказал ни слова об описании интерфейсов функций. Я просто не мог оставить этот факт без внимания, поскольку лично мне уже набила оскомину проблема отсутствия описания интерфейсов функций в коде.
Если вы пишете на строготипизированном языке, то у вас нет проблем с тем какого типа параметры функция принимает и возвращает. Это все есть прямо в описании функции. 

int addition (int a, int b) {
    return a + b;
}


В данном примере видно, что функция принимает два целочисленных параметра и возвращает целочисленный параметр. При наличии хорошего названия у функции, можно сразу же понять, что ей надо передать и что при этом получишь взамен.
Если же вы пишете код на javascript или perl, то все становится несколько иначе. Javascript не является строготипизированным языком и при описании функций в нем не указываются типы параметров. Из-за этого может возникать ряд проблем.

function getTimeInterval (start, end) {
     …
}

Как вы думаете какого типа должны быть start и end? Timestamp? Дата в виде строки? Объект Date? Для того, чтобы понять, какого типа параметры передавать функции, надо смотреть в ее тело и разбираться в том, как она работает. В зависимости от сложности кода, на подобные вещи может уходить немало времени и сил. Ведь так гораздо понятнее:


/**
 * Получить разницу между датами в секундах
 * @param {Timestamp} start Начало интерала
 * @param {Timestamp} end Конец интерала
 * @return {Int}
 */
function getTimeInterval (start, end) {
     …
}

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

А вот в случае с perl все обстоит еще хуже. У perl при описании функций нет строгих требований в указании параметров. Поэтому параметры в теле функции вполне могут выглядеть как $_[1], $_[2], @_, shift. Конечно, во всех bestpractices рекомендуется в самом начале функций извлекать все переменные из @_ и давать им осмысленные имена, но  данные указания не всегда соблюдаются и это не избавляет нас от проблем с типами переменных.

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


# Получить разницу между датами в секундах
# param {Timestamp} $start Начало интерала
# param {Timestamp} $end Конец интервала
# return {Int}
sub get_time_imterval {
    my ($start, $end) = @_;
    ...
}

Самый частый аргумент, который я слышу против - это очень долго писать, эти комментарии быстро устаревают. Ничего подобного. Описание интерфейсов функций экономит массу времени другим программистам. Вместо того, чтобы разбираться в чужом коде, пытаясь понять, какие параметры принимает функция, можно быстро посмотреть на комментарий и начать использовать функцию. Доводы вроде: когда человек просматривает чужую функцию, он автоматически делает ревью его кода, не являются разумными доводами. Когда человек начинает разбираться в чужом коде, он отвлекается от своей задачи, выгружает свою часть предметной области и загружает чужую. И если команда достаточно большая, то на разбирательство с чужим кодом может уходить масса времени.

Ну, и, разумеется, комментарии к интерфейсам функций можно использовать для автоматической генерации документации. Лучше не изобретать свой велосипед в стиле описания интерфейсов, а воспользоваться готовым. На javascript для этих целей есть jsdoc и масса инструментов, позволяющих создавать документацию в виде html-страниц. Для perl есть разметка perlpod. Но, увы, написание и чтение perlpod-комментариев, на мой взгляд, имеет смысл лишь в том случае, если вы собираетесь делать автогенерацию документации. В остальных же случаях, это удовольствие для людей склонных к извращениям.

Итого: возьмите себе за правило описывать интерфейсы функций. Это очистит вашу карму и позволит продвинуться дальше на пути к нирване.