Tuesday, October 15, 2013

Сверху вниз и снизу вверх. Часть V

Cм. также
Оптимистический и пессимистический взгляд на жизнь
Сверху вниз и снизу вверх. Часть I
Сверху вниз и снизу вверх. Часть II
BFS and DFS - поиск в ширину и глубину Сверху вниз и снизу вверх. Часть III (продолжение)
Testing First Сверху вниз и снизу вверх. Часть IV

Прошло три года со дня написания последней части. :-)
Завершающая заметка в этой серии. В ней речь пойдёт о технике Note first - сначала писать комментарии и её связи с основной мыслью серии заметок.

Кратко опишу разные типы комментариев, которые должны быть в хорошем коде. Во-первых, это javadoc - комментарии сверху функции и класса, которые описывают, что данная функция или класс делают. Замечу, что часто javadoc является избыточным * , так как имя функции и её параметры однозначно указывают что эта функция делает. Типичные пример, getWeight(). javadoc предназначен для пользователей этого класса, программистов, которые пишут другой модуль и которые хотят использовать написанный вами код. Они должны чётко представлять что делает каждая ваша функция, но их, в общем случае, не интересует как она это делает.

Ниже есть продолжение.

Примечание : * - часто javadoc является и вредным. Известно, что код программы имеет тенденцию сильно меняться. При чём это относится не только к тому как что-то делается (имплиментация), но и что делается (API). В таких случаях, довольно часто разработчик забывает обновить javadoc. Таким образом другому разработчику неадекватный javadoc не только не помогает, но мешает разобраться с тем, что делает тот или иной модуль. В тех случаях, когда javadoc сообщает только ту информацию, которую можно и так получить при анализе function signature, имеет смысл намеренно не писать такой javadoc.

Остальные типы комментариев поясняют как достигается тот или иной результат. Лично я грубо разделил бы эти типы, на комментарии для меня и комментарии для других, кто будет читать этот код. Поясню. В коде может быть написано какая-то нетривиальная конструкция, к примеру

long valBits = ...
int sign = ((valBits >> 63)==0 ? 1 : -1);
int exponent = (int) ((valBits >> 52) & 0x7ffL);
long significand = (exponent==0 ? (valBits & ((1L<<52) - 1)) << 1
: (valBits & ((1L<<52) - 1)) | (1L<<52));
(с) java.math.BigDecimal(double val) Понять что тут происходит очень сложно. Однако, перед этим блоком есть комментарий
// Translate the double into sign, exponent and significand, according
// to the formulae in JLS, Section 20.10.22.
Теперь становится понятней, речь идёт об арифметике с плавающей запятой, в неё число записывается при помощи экспоненты, мантисы и т.п. При этом есть фиксированное количество знаков выделенное для каждого из них (в это разгадка использования "магических чисел"!). Обычно этого достаточно, если же нужны дополнительные подробности в данном конкретном примере есть отсылка к Java Language Specification. Это пример комментария для других. Хотя я не раз сталкивался с тем, что через полгода-год я забывал смысл подобных манипуляций и с удовольствием читал этот "комментарий для других". Лично я такого рода комментарии никогда не опускаю, какая бы проблема со временем не была. Последний тип комментария, это описания алгоритма. Поясню о чём идёт речь. Вы пишите функцию, которая сначала делает что-то одно, потом другое, потом третье, потом делает какие-то сложны проверки и объединяет полученные результаты. Когда вы читаете такой код, вам нужно прочитать его весь, чтобы понять что тут вообще происходить. Лично я считаю, что такого рода пояснения должны быть частью javadoc. Однако, есть случаи когда это является implementation details и поэтому вы не хотите это писать в javadoc. Вы хотите оставить себе свободу изменить их, это не является частью вашего "контракта". Правильнее будет в таком случае сделать refactoring - разбить одну большую функцию на много маленьких private functions. Оставшееся функция должна быть достаточно маленькой, чтобы её можно было легко и быстро прочитать и, не в даваясь в детали, понять что же алгоритм делает. Таким образом мы не только решаем проблему, но и улучшаем качество кода. Напоследок я хочу сказать, что иногда в коде делают "магические" вещи. К примеру, берут id одного объекта и используют его в качестве id другого. В таких случаях, лично я предпочитаю, в каждом таком случае ставить комментарий, мол, этот id используется как id другого объекта. Писать в каждом месте почему это корректно не стоит, количество "лишних" строк от этого только увеличится, но в одном каком-нибудь "центральном", в некотором смысле, смысле нужно (к примеру, есть триггер в DB который присваивает тот же ID). Так вот, техника Note first говорить, что перед написанием функции со сложным алгоритмом, сначала написать комментарий, которой опишет этот алгоритм, т.е. написать пустую функцию с комментарием. Затем написать код, который бы вызвал функции-заглушки, затем детализировать все эти функции-заглушки (второй уровень) и т.д. По сути, это является небольшой вариацией техники сверху-вниз. При использовании техники снизу-вверх этот комментарий является лишним. В лучшем случае, но будет "размазан" по javadoc вспомогательных функций, написание такого комментария вообще не требуется.