Если вам захочется узнать, какие конвенции оформления использую я, обратитесь к переработанному коду в листингах Б.7 (с. 442) — Б.14.
G25: Заменяйте «волшебные числа» именованными константами
Вероятно, это одно из самых древних правил разработки. Помню, оно встречалось мне еще в 60-х годах, в учебниках COBOL, FORTRAN и PL/1 для начинающих. В общем случае присутствие «сырых» чисел в коде нежелательно. Числа следует скрыть в константах с содержательными именами.
Например, число 86,400 следует скрыть в константе SECONDS_PER_DAY. Если в странице отчета выводится 55 строк, число 55 следует скрыть в константе LINES_PER_PAGE.
Некоторые числа так легко узнаются, что их не обязательно скрывать за именованными константами — при условии, что они используются в сочетании с предельно ясным кодом. Пример:
double milesWalked = feetWalked/5280.0;
int dailyPay = hourlyRate * 8;
double circumference = radius * Math.PI * 2;
Нужны ли константы FEET_PER_MILE, WORK_HOURS_PER_DAY и TWO в этих примерах? Разумеется, последний случай выглядит особенно абсурдно. В некоторых формулах константы попросту лучше воспринимаются в числовой записи. По поводу WORK_HOURS_PER_DAY можно спорить, потому что законы и нормативы могут изменяться. С другой стороны, формула с числом 8 читается настолько удобно, что мне просто не хочется нагружать читателя кода лишними 17 символами. А число 5280 — количество футов в миле — настолько хорошо известно и уникально, что читатель сразу узнает его, даже если оно будет располагаться вне какого-либо контекста.
Такие константы, как 3.141592653589793, тоже хорошо известны и легко узнаваемы. Однако вероятность ошибки слишком велика, чтобы оставлять их в числовой форме. Встречая значение 3.1415927535890793, вы сразу догадываетесь, что перед вами число π, и не проверяете его (а вы заметили ошибку в одной цифре?). Также мы не хотим, чтобы в программах использовались сокращения 3.14, 3.14159, 3.142 и т.д. К счастью, значение Math.PI уже определено за нас.
Термин «волшебное число» относится не только к числам. Он распространяется на все лексемы, значения которых не являются самодокументирующими. Пример:
assertEquals(7777, Employee.find("John Doe").employeeNumber());
В этом проверочном условии задействованы два «волшебных числа». Очевидно, первое — 7777, хотя его смысл далеко не так очевиден. Второе «волшебное число» — строка "John Doe". Ее смысл тоже выглядит весьма загадочно.
Оказывается, "John Doe" — имя работника с табельным номером 7777 в тестовой базе данных, созданной нашей группой. Все участники группы знают, как подключаться к этой базе данных. В базе уже хранятся тестовые записи с заранее известными значениями и атрибутами. Также выясняется, что "John Doe" — единственный работник с почасовой оплатой в тестовой базе данных. Следовательно, эта проверка должна выглядеть так:
assertEquals(
HOURLY_EMPLOYEE_ID,
Employee.find(HOURLY_EMPLOYEE_NAME).employeeNumber());
G26: Будьте точны
Наивно ожидать, что первая запись, возвращаемая по запросу, является единственной. Использовать числа c плавающей точкой для представления денежных сумм — почти преступление. Отсутствие блокировок и/или управления транзакциями только потому, что вы думаете, что одновременное обновление маловероятно — в лучшем случае халатность. Объявление переменной с типом ArrayList там, где более уместен тип List — чрезмерное ограничение. Объявление всех переменных защищенными по умолчанию — недостаточное ограничение.
Принимая решение в своем коде, убедитесь в том, что вы действуете предельно точно и аккуратно. Знайте, почему принимается решение, и как вы собираетесь поступать с исключениями из правила. Не ленитесь обеспечивать точность своих решений. Если вы решили вызвать функцию, которая может вернуть null — проверьте возвращаемое значение. Если вы запрашиваете из базы данных запись, которая, по вашему мнению, является единственной — проверьте, не вернул ли запрос дополнительные записи. Если вам нужно работать с денежными суммами, используйте целые числа и округляйте результат по действующим правилам. Если в программе существует возможность одновременного объявления, реализуйте ту или иную разновидность блокировки. Неоднозначности и неточности в коде объясняются либо недопониманием, либо ленью. В любом случае от них следует избавиться.
G27: Структура важнее конвенций