Pull to refresh

Точки выхода или немного о структурном программировании

Reading time 3 min
Views 15K
Читая Хабрахабр или просматривая чужие исходные коды, мне довольно часто приходится замечать примерно следующий кусок кода, который совершенно одинаково «звучит» на любом языке, на каком бы не был написан:
function(single_document)
{  
  if (single_document.getElementById("comments") != null)
    return;
    …
    …
    …
    …
}
* This source code was highlighted with Source Code Highlighter.

Здесь приведён кусочек кода на Javascript, но то же самое можно написать на нескольких десятках других языков. Что здесь не так? Только то, что у функции (метода, свойства, процедуры) несколько точек выхода. Если вам интересно почему это плохо, прочитай то что написано под катом.

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

Слава богу, уже почти не встретишь в коде использования оператора goto, хотя и такое встречается. Но чаще не соблюдаются многие другие менее очевидные принципы структурного программирования. Один из таких принципов, авторство которого приписывается Эдсгеру Дейкстре звучит так:

«модуль (в данном случае функция) должен иметь только одну точку входа и только одну точку выхода»



В связи с этим, использование оператора return в приведённом примере противоречит принципу структурного программирования. Что такого хорошего в этом принципе?
Причины из-за которых следует придерживаться этого принципе следующие:
  • сложность отладки такого кода, не всегда очевидно когда произошёл возврат из функции, к примеру мы имеем несколько return и никогда точно не можем сказать какой из них был вызван;
  • проблемы с сопровождением и изменением. При взгляде на код, особенно, если он большой не всегда заметны существующие точки выхода. В связи с этим поведение добавленного кода может быть непредсказуемо, не известно достоверно, будет ли выполнен ваш добавленный в конец функции код или нет. Для того, чтобы быть уверенным в этом, вам необходимо однозначно оценить все точки выхода, понять логику, а возможно и внедрить перед каждой точкой выхода вызов вашего кода.

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

Как стоит переписать представленный код:
function(single_document)
{  
  if (single_document.getElementById("comments") == null)
    {
     …
     …
     …
     …
    }
}
* This source code was highlighted with Source Code Highlighter.

Представленный код является минимальным по смыслу и содержанию, нужно понимать, что он призван показать принцип, а не продемонстрировать результат. Многие могут сказать, что единичный возврат из метода или функции в самом начале кода — это обычная практика и что проблем не бывает и что постоянно используется. Я не буду отговаривать, но хотел бы напомнить, что принципы на то и принципы, чтобы их придерживаться всегда, а не только по требованию. Во-первых, это воспитывает, во-вторых, некоторым образом улучшает код, который всегда будет строиться по одним принципам, без нарушений. Ну и в-третьих, привычка писать правильно — это то, к чему должен стремится каждый, не так ли?
Tags:
Hubs:
+10
Comments 167
Comments Comments 167

Articles