Pull to refresh

Совершенный код

Reading time 4 min
Views 19K
Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live.
Damian Conway, co-designer of Perl 6

Хороший программный код определяется как минимум тремя признаками: однозначность, эффективность и сопровождаемость.

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

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

Сопровождаемость заключается в том, что код пишется в первую очередь для тех, кто будет его сопровождать. Сопровождаемость – легкость использования написанного кода, минимизация возможности появления ошибок при его изменении.

Однозначность кода


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

А между тем, все существующие методики улучшения кода (и эта статья тоже) направлены на облегчение жизни самих же программистов. Например, чтобы ваш код был понятен другим программистам (и вам же самим спустя N месяцев после его написания), необходимо соблюдать стиль кодирования. Из предложенных стилей для используемого языка лучше выбрать тот, в котором рекомендуется форматирование кода в стиле K&R (по инициалам Kernighan и Ritchie, авторов книги о языке Си), потому что оно отличается от остальных лучшей читаемостью и делает код более гибким.

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

Для имен функций, переменных и констант выбирайте такие имена, чтобы их предназначение было очевидно без сопровождающего комментария (что вовсе не избавляет от необходимости писать комментарии). Из сокращений и аббревиатур допускается употреблять только общепринятые и общеизвестные: min, max, avg, sum, len, ctrl, src, msg и другие. Если вы не можете подобрать сокращенную форму названию переменной, оставьте как есть.

Используйте именованные константы, избегайте голых цифр и строковых литералов в коде. Даже если для вашего алгоритма понятно предназначение каждого числа, используемого в коде в чистом виде, корректировка алгоритма может сломать ваш код, когда потребуется изменить одно число на другое. При использовании именованных констант достаточно будет изменить число в одном месте, не трогая сам код алгоритма. Я бы не рекомендовал делать исключения даже для общеизвестных значений, таких как 3.14, 2.7, 9.8, 42.

Эффективность кода


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

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

Прежде чем изобретать велосипед, нужно убедиться, что этого еще не сделали до вас. И только если велосипедов нет, или они не подходят вам в силу каких-либо причин (кроме причины «это написано не мной»), можно приступать к реализации. Использование сторонних библиотек позволяет сосредоточиться на собственном алгоритме. Чем популярнее библиотека, тем более эффективный код в ней используется и тем меньше вероятность обнаружить в ней ошибки. Если интерфейс библиотеки громоздок или слишком унифицирован, напишите интерфейс-обертку для этой библиотеки, чтобы ваш код был в одном стиле.

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

Сопровождаемость кода


При внесении изменений в код всегда есть вероятность сломать его, поэтому из всех альтернативных вариантов алгоритма всегда используйте максимально гибкий. Например, вместо нескольких else-if или switch бывает гораздо удобнее использовать поиск по ключу в хэш-таблице. В таких случаях, чтобы добавить еще одно значение в список альтернатив, достаточно добавить еще одно значение в хэш, не касаясь самого алгоритма поиска.

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

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

Вне зависимости от сложности кода, комментарии должны присутствовать обязательно. Как минимум — в заголовке каждой функции/метода: что делает, какие параметры принимает, что возвращает. Без коментариев можно оставлять только самые очевидные части алгоритма, нет смысла комментировать каждое выражение. Самый простой способ определить, требует ли алгоритм подробных коментариев — прикинуть, сколько секунд необходимо, чтобы понять его. Если больше 5 — комментарии необходимы.
Tags:
Hubs:
+52
Comments 61
Comments Comments 61

Articles