Нелюбовь идеологов структурного программирования к оператору перехода goto уже давно стала своего рода их визитной карточкой. Как это нередко бывает, чисто логические, абстрактные умозаключения, выраженные людьми, пользующимися в определенных кругах заслуженным авторитетом, в предельно простой форме "черное - белое", нередко превращаются в полурелигиозные догматы, причем их приверженцы и слышать ничего не хотят про аргументированные возражения, пропуская их мимо ушей. Если вдруг в окружении "фанатов" структурного программирования оказывается человек, в программе у которого обнаруживается не один, а два, три или пять операторов goto, то он немедленно подвергается обструкции. В лучшем случае ему предлагается почитать учебники пятнадцатилетней давности, которые до сих пор считаются "классикой жанра", хотя с тех пор на свет родились языки совсем с другой идеологией, да и операционные системы уже давно заставили программистов перейти от линейной структуры программы к событийно-ориентированной.
Когда лучшие программисты мира встречают аплодисментами объявление о том, что в стандарт языка Java не включен оператор перехода, то создается впечатление, будто до этого он так сильно мешал бедным разработчикам, что все ошибки в программах были именно следствием его наличия в языке! Хотя ничто, собственно, не заставляло их использовать этот оператор в своих программах. Аплодисменты, очевидно, были скорее выражением радости в отношении более "отсталых" коллег, которые все еще смели использовать этот оператор и которым теперь наконец придется переходить на более прогрессивные технологии.
Спору нет, часто и бессистемно используемый goto действительно затрудняет чтение чужого кода, делая программу неразборчивой и трудно отлаживаемой. Однако в операторе перехода ли дело?
В недавнем номере одного компьютерного журнала автор статьи, посвященной принципам программирования, детально описывает наиболее оптимальную, на его (и мой) взгляд, управляющую семантическую структуру - расширенный оператор CASE, позволяющий производить выбор по любым условиям (например, X > 0; (A[J] != N) AND (T = TRUE)), а не только по значениям конкретной переменной, что делает ненужными все остальные структуры. Реализовать этот выбор можно самыми разными способами, надо только избежать побочных эффектов, связанных с порядком вычисления условий выбора. Автор приводит пример программы, которая, по сути, является "программой с переходами" и тем не менее вполне наглядна и легко читаема. Обычные же структурные элементы ярых сторонников Java только портят степень ее "прозрачности". Конечно, и в этом языке есть оператор break, который позволяет красиво описать решение, но он-то как раз наравне с continue и return сам противоречит идеологии структурного программирования! Если быть последовательным, то вместе с goto надо было удалить из Java и эти три оператора и получить нечто напоминающее первоначальный вариант Паскаля г-на Вирта.
Но проблема с оператором goto лежит гораздо глубже, чем может показаться на первый взгляд. Автор вышеупомянутой статьи отмечает, что "структурные управляющие конструкции - это завуалированные переходы к меткам". Я же, полностью с этим соглашаясь, рискну также высказать и обратное утверждение: "Переходы к меткам суть завуалированные структурные управляющие конструкции". Когда реально используется goto? В абсолютном большинстве случаев - после условного оператора IF. Конечно, можно представить программу такого вида:
DO1
GOTO A
B:
DO3
GOTO C
A:
DO2
GOTO B
C:
DO4
но она, очевидно, преобразовывается в
DO1
DO2
DO3
DO4
Таким образом, можно утверждать, что наибольший вред наглядности и надежности программы наносит отнюдь не GOTO, а оператор IF!
Собственно, все исследователи, занимающиеся проблемами тестирования программ, отмечают, что основным источником логических ошибок является слишком запутанная операторами IF структура программы, не позволяющая эффективно проводить полное тестирование и верификацию. Можно ли обойтись без этого оператора? Безусловно. Хотя в чистом виде расширенный оператор CASE не реализован, насколько мне известно, ни в одном из существующих языков программирования, но приблизить внешний вид программы к его структуре вполне возможно. В Си этому поможет оператор break, а в BASIC воистину вместо IF лучше использовать GOTO!
Конечно, совсем обойтись без этого оператора, скорее всего, не получится. Однако всегда нелишне подольше поработать над структурой программы, пытаясь сделать ее максимально линейной, чем потом мучиться часами в отладчике. Весьма желательно, в частности, не допускать двукратного и более высоких уровней вложенности условных операторов, стараясь понизить "размерность" программного кода. Конечно, если в одном из условий вызывается подпрограмма, в которой, в свою очередь, имеется оператор IF, то это следует считать двойным вложением.
Идея расширенного оператора CASE на самом деле уже давно неявно используется в современном программировании. В той же Windows (или OS/2 - кому что нравится) вместо ДОСовской модели последовательного выполнения приходится программировать реакции на различные события, коих насчитываются сотни, причем по происхождению события эти довольно сильно отличаются друг от друга. Иными словами, это все тот же расширенный CASE! И практически все системы визуального программирования, такие, как Visual C++, Visual Basic, Delphi, а также программные комплексы более высоких уровней используют ориентированную на управляющие события модель, что позволяет получать значительно более надежный код. Этот факт, собственно, лишний раз подтверждает правильность высказанного достаточно давно, правда, в неявной форме, утверждения о вреде оператора IF, которое внешне выразилось в столь ожесточенном преследовании любителей GOTO. Если бы такие репрессивные меры применялись к условному оператору, то оператор перехода уже давно исчез бы сам собой, а мы с вами - In My Humble Opinion - стали бы свидетелями нового поколения языков, позволяющих создавать программы со стремящимся к нулю количеством ошибок.
Сергей Бобровский
К Сергею Бобровскому можно обратиться по адресу: sergei95@glas.apc.org.