Читаемый код или программирование как искусство. Часть 2
Продолжаем читать книгу «Читаемый код или программирование как искусство».
В прошлый раз мы обсудили 1–4 главы: названия переменных и функций, их двусмысленность, эстетику кода и простоту. В этой заметке — главы 5–7:
Глава 5. Комментарии нужны, чтобы читатель знал столько же, сколько автор
Кратко:
- Если комментарий не несёт дополнительной информации, он не нужен;
- Объясняйте недостатки кода, поясняйте константы;
- Рассказывайте «зачем это», а не «что это»;
- Хороший код лучше, чем плохой код с хорошими комментариями;
- Думайте, как новичок.
Комментарии занимают место на экране и время, чтобы их прочесть. Не надо комментировать то, что понятно из кода.
Плохие названия переменных или функций нужно не комментировать, а исправлять. Комментарий видно только в одном месте, а название — везде. В первом примере комментарий пытается прояснить ситуацию, во втором название делает всё само:
// На самом деле не удаляет запись, а завершает обработку
void DeleteRegistry(RegistryKey* key);
void ReleaseRegistryHandle(RegistryKey* key);
Если за решением стоит какая-то история, о ней лучше рассказать:
// внезапно бинарное дерево в этом случае позволило ускорить алгоритм на 40%
Комментарии могут предупреждать читателя о недостатках или рассказывать, почему использовано именно это решение:
// при аргументе, большем 500, возвращает округлённое значение
// другие алгоритмы работают в два раза медленнее
У констант обычно есть история, как они появились. О ней тоже можно рассказать:
// оптимальное соотношение веса и качества
const IMAGE_QUALITY = 0.72;
Или константа может быть приближённой:
// больше всё равно никто не прочтёт
const MAX_RSS_SUBSCRIPTIONS = 1000;
Хорошая практика — думать, что потребуется объяснить новичку в проекте. Например, мысли «это связка между бизнес-логикой и БД» или «это механизм кэширования, об остальной части системы он ничего не знает» — именно то, что следует записывать.
Глава 6. Комментарии должны быть короткими
Кратко:
- Рассказывайте на примерах;
- Объясняйте «магические аргументы»;
- Сокращайте.
Объяснить работу функции проще всего на примере. Здесь непонятно, уберёт ли функция только точные совпадения с chars, или просто символы, из которых chars состоит:
// Убирает сочетания 'chars' из входного 'src'.
String Strip(String src, String chars) { ... }
Так понятнее:
// Например: Strip("abba/a/ba", "ab") вернёт "/a/"
String Strip(String src, String chars) { ... }
В языках, где нельзя указывать именованные аргументы, у функций часто появляются «магические аргументы»:
connect(10, false);
Что означает 10 и false, без документации непонятно. Если название функции изменить нельзя, можно использовать комментарии для прояснения:
connect(/* timeout_ms = */ 10, /* use_encryption = */ false);
Но такой комментарий должен стоять до аргумента. Иначе будет совсем непонятно:
// «использовать или нет?..»
connect( ... , false /* use_encryption */);
// «эм, false — значит использовать?..»
connect( ... , false /* = use_encryption */);
Сокращайте комментарии и подбирайте для них точные слова.
Глава 7. Пишите так, чтобы не приходилось перечитывать
Кратко:
- Сравнивайте изменяющееся с постоянным, а не наоборот;
- Используйте ранний выход;
- Аккуратнее с тернарными операторами;
- Уменьшайте вложенность.
Порядок операндов в сравнении важен. Слева лучше ставить то, что изменяется, справа — что постоянно:
// так хорошо
if (length >= 10) {...
// так плохо
if (10 <= length) {...
Если изменяются оба значения, то справа надо ставить то, что более постоянно:
// так хорошо
while (bytes_received < bytes_expected)
// так плохо
while (bytes_expected > bytes_received)
Проверяйте самые простые случаи вначале, чтобы быстрее выйти из условия или функции:
if (!response) return null
try {
response.getHeader()
...
}
...
Тернарные операторы — ок. Но если выражение с ним непонятное, замените на if ... else:
// это ок
time_postfix = (hour >= 12) ? "pm" : "am";
// а это нет
return exponent >= 0 ? mantissa * (1 << exponent) : mantissa / (1 << -exponent);
Уменьшайте вложенность кода — пишите обёртки и вспомогательные функции, используйте ранний выход. Если не получается проследить, как выполняется функция, упростите её.
Что дальше?
В следующий раз обсудим:
- Как разбивать большие выражения на куски;
- Переменные, и как они влияют на читаемость;
- Лишнюю функциональность;
- Приём «одна задача за раз».