четверг, 23 мая 2013 г.

Как повысить качество программного кода (несколько советов)


На вскидку несколько основных правил, позволяющих повысить качество программного кода (самому вспомнить и, возможно, будет интересно кому-то ещё). Следует уточнить, что эти рекомендации проверены годами и исходят от авторитетных программистов, например от Б. Страуструпа (создатель языка C++):


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

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

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

Если ты в настоящий момент не можешь проверить правильность результата, возвращаемого написанным тобой методом\функцией (по разным причинам), то всегда выполняй оценку кода путём примерного вычисления, выполненного вручную. Например, если твой метод должен посчитать, можно ли на автомобиле за 6 часов доехать от Краснодара до Сочи, то с помощью Google ты можешь выяснить, что расстояние равно примерно 800 км. Теперь, назначив (приблизительно) реальную среднюю скорость, с которой автомобиль может ехать по дороге (например, 90 км/ч), ты сможешь дать предварительную оценку о том, является ли ответ, выданный твоим кодом, разумным или нет. В данном случае тебе нужно убедиться, что ответ, как минимум, не является глупым.

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

Никогда не присваивай в своём коде переменным имена, отличающиеся лишь регистором. Например: name и Name. Не следует назначать имена, начинающиеся с символа"_", т.к. подобные имена часто генерируются компилятором, что может привести к конфликтам. Не следует использовать слишком длинные имена, т.к. их утомительно набирать. Не следует назначать имена, состоящие только из прописных букв, т.к. такие имена принято назначать в директивах препроцессора различным макросам. Никогда не создавай имён, которые можно ошибочно написать\прочитать. Например: foll, f0ll, fo11, и т.п. Не создавай имён, при чтении которых можно перепутать такие буквы: I, l, 1, o, O, 0. Имена, которые ты создаёшь, должны быть понятны и, по возможности, кратки. Не следует создавать в коде имён в виде акронимов (если область их видимости высока), например (abcd, tdr и т.п.), т.к. спустя некоторое время ты и сам можешь забыть, что они означают. Однако, в небольших блоках кода краткие имена вполне приемлемы.

Всегда старайся разбивать задачу на более мелкие подзадачи. Маленькие подзадачи гораздо легче осмыслить, проверить и найти в них ошибки. Каждый метод должен решать свою маленькую задачку. Не следует писать в методе код, который сразу поёт, танцует и бегает тебе за пивом. Такой код 100% будет подвержен большому количеству ошибок, которые на первый взгляд можно и не выявить. Конструируй код из таких маленьких "кубиков" и его будет легче читать\проверять.

Если в коде присутствует фрагмент, который дублируется в нескольких местах, то его следует выносить в отдельную функцию.

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

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

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

Код должен быть простым и понятным. Не следует писать заумных инструкций, поскольку они будут трудны для понимания и, как следствие, более подверженны ошибками, которые будет трудно выявить.

"Последняя ошибка" - это шутка программистов. Такой ошибки не существует. В больших программах никогда невозможно найти последнюю ошибку.

Если в выражении значение переменной меняется, то не следует использовать её в этом выражении более одного раза, т.к. порядок вычисления не определён. Это может привести к получению разных результатов на разных компьютерах и даже на одном и том же, в разных сессиях работы. Например, не стоит писать так: items[++i] = i;. Или так: Console.WriteLine("{0}, {1}", i, ++i);.

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

Обязательно в начале программы должен быть комментарий с информацие следующего рода:
1. Название программы.
2. Автор программы.
3. Назначение программы.
4. Причины, по которым программа была написана.
5. История версий программы и изменений в ней.
6. Описание структуры программы и принципа её работы.
7. Информация о входных данных
8. Информация о данных, получаемых на выходе.
9. Информация об обнаруженных проблемах, и ошибках.

Отделяй интерфейс от его реализации. Это сделает твой код более понятным и менее подверженным ошибками.

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

Приступая к решению задачи, не следует считать, что ты хорошо её знаешь. Это не так и ты сможешь убедиться в этом в процессе написания кода. Как правило, в процессе написания на свет вылезают различные "тёмные углы", о которых не задумывался ранее. Начинай писать код с простого, небольшого первого варианта. Если в процессе разработки видишь, что пошёл по неверному пути - выбрасывай в ведро этот вариант и пиши новый. Не следует пытаться вносить изменения в данный, т.к. в конце концов запутаешься и результат будет похож на больного, забинтованного по самые уши. Как только получишь и протестируешь более менее работающую первую версию, выдающую корректные результаты, приступай к её оптимизации: разноси код по функциям, форматируй код, добавляй комментарии.

Не следует в первой версии стараться решить сразу все вопросы. Чем меньше вопросов решается в первой версии, тем проще будет её написать и тем качественней она получится. Наращивая функционал следует постепенно, от версии к версии (как жемчужину в ракушке).

Не создавай слишком больших классов, т.е. классов в интерфейсе которых огромное количество функций. Такие классы программисту сложнее запоминать и работать с ними. Добавлять нужно только тот функционал, который действительно нужен и восстребован. Если какой-то функционал мало восстребован - не следует включать его в состав интерфейса (класса), дабы не захламлять его. В интерфейсе должен быть минимальный набор функционала, позволяющий решать поставленную задачу. Всё должно быть по спартански лаконично и понятно.

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

Код программы должен отлавливать все исключения, дабы программа не закрывалась внезапно, оставляя пользователя в недоумении. Поэтому метод main должен выглядеть примерно так (C++):


   1:  int main(int argc, char** argv)
   2:  try{
   3:      // Здесь основной код.
   4:  }
   5:  catch(exception& e){
   6:      std::cerr << e.what() << endl;
   7:      return 1;
   8:  }
   9:  catch(...){
  10:      std::cerr << "Unknown exception." << endl;
  11:      return 2;
  12:  }


Это то, что вспомнилось на вскидку...

Комментариев нет: