Главная » Delphi » Немного о стилях программирования

0

Принцип, который автор этих строк всегда старался соблюдать, как при составлении программ, гак и при разработке электронных схем — "не умножать сущностей без необходимое™". Должна быть четко поставлена цель, и она должна быть достигнута минимально возможными средствами. Однако помните, что на пути упрощения вас всегда подстерегает опасность что-то не предусмотреть или забыть, и если есть такая опасность, то лучше, как говорится, "перебдеть, чем недобдеть". Так, в главе 20 при чтении из последовательного порта для сокращения текста программы мы пренебрежем рекомендуемыми проверками типа:

if not ClearConrcError(hPort, dwEiror,                         ) then

raise Exception.Create(‘Error clearing port’);

Автор этих строк много раз пытался искусственно вызвать ситуацию, когда указанная функция clearconimsrror и аналогичные ей возвращают ошибку, и ему это не удалось. Поэтому он сделал вывод, что на практике такая ошибка крайне маловероятна. И все же, если вы делаете программу для широкого распространения, этими проверками ни в коем случае пренебрегать нельзя: мало ли что может случиться, если программа выйдет из-под вашего контроля. Однажды мне пришлось столкнуться со случаем (единственный, правда, раз в жизни), когда заведомо существующий коммуникационный порт не поддавался инициализации никакими способами. И если в программе не было нужных проверок, то она просто "висла", а это недопустимо.

Несколько слов об организации циклов. Те, кто хоть раз пробовал программировать на низкоуровневом ассемблере, знают, что никаких операторов while, for, if.. .than и других подобных атрибутов языков высокого уровня для процессора не существует. Любое подобное действие осуществляется на самом деле с помощью условных операторов перехода на метку, а еще точнее— на нужную команду по ее адресу (в этом смысле канонический Basic, столь нелюбимый профессионалами, был даже ближе к ассемблеру, чем С или тем более Pascal). Покажем, как, например, выглядит на Intel-ассемблере цикл if… then.. .else. Пусть есть переменная varx, константа const 1 и две процедуры: pr_l и рг 2. Нам требуется выполнить следующую часто встречающуюся задачу: если переменная var х больше const i, то обнулить переменную varx и перейти к процедуре рг_1, иначе выполнить процедуру рг_2. После выполнения той или иной процедуры требуется, естественно, продолжить выполнение основной программы. Соответствующий программный код может быть, например, таким:

стпр var x, const ! ; сравниваем ja rnetkal ;если больше, то на матку 1 call сг_2 /иначе вызываем процедуру 2 jmp rcstka? ;после нее на продолжение metksi: хог ах,ах ;обнуляем регистр ах mov varx,ах ;переменная = О call рг_! ;вызываем процедуру 1 melka2: <продолжение основной про1р<>ммы>

А вот та же самая задача, но реализованная на языке Pascal:

if (var_x>const_l) then begin var_x:=0; pr_l end else pr2;

[1] la этом примере очень хорошо видны преимущества языков высокого уровня. Знаменитый лозунг Эдгара Дейкстры "программирование без goto", таким образом, предполагает фактически использование эмуляторов действий, связанных с использованием условных и безусловных переходов, но зато следование ему значительно повышает читаемость программ. И все же оператор goto включен во все современные языки, и никто вам не может запретить его использовать. Для людей, которые имеют образно-аналитический склад мышления, иногда проще разобраться в хитросплетениях условных и безусловных переходов, чем вникать, к примеру, в семантические различия между циклами типа while. . .do И repeat. . .until.

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

Немного о локальных и глобальных переменных. Автор этих строк предпочитает везде, где только можно, для передачи параметров пользоваться глобальными переменными— вместо того, чтобы организовывать процедуры с формальными» параметрами. Это. разумеется, не руководство к действию, а просто привычка — на мой взгляд, так повышается читаемость и прозрачность программы. Разумеется, при использовании глобальных переменных для организации, например, циклов вы легко можете наделать ошибок, в причинах которых иногда непросто разобраться, недаром Delphi выводит даже специальное предупреждение на этот счет. Нет никаких особых причин, кроме указанных, для того чтобы следовать тому или иному стилю программирования, только при использовании локальных переменных автор настоятельно рекомендует по возможности присваивать им имена, не совпадающие ни с другими локальными переменными, ни с глобальными, иначе в тексте вашей программы не разберется и сам Валерий Васильевич Фаромов.

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

Источник: Ревнч Ю. В.  Нестандартные приемы программирования на Delphi. — СПб.: БХВ-Петербург, 2005. — 560 е.: ил.

По теме:

  • Комментарии