Шаг 53.
О стиле программирования

    На этом шаге мы рассмотрим понятие стиля программирования и приведем несколько рекомендаций по его улучшению.

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

    Дженкинс и Глазго считают, что большинство программистов в своей работе используют один язык программирования и программируют в условиях (среде, возможностях), навязанных этим языком, оставляя в стороне альтернативные подходы к цели. Поэтому им трудно увидеть преимущества другого стиля при решении конкретной задачи [1]. Бобров и Стефик выделили пять основных разновидностей стилей программирования (в скобках указаны соответствующие виды абстракций):

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

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

    К сожалению, взгляд на стиль программирования как на стиль мышления еще не всеми и не до конца осознан. Очень часто стиль программирования сводится к технологии программирования. За последние годы мы пережили несколько вспышек увлечения модными методологиями и технологиями программирования, такими например, как структурное программирование. Но в главном все они ориентируются на внешние факторы, характеризующие программу. Кстати сказать, жертвой структурного программирования чуть было не стали FORTRAN и BASIC с их неструктурными операторами. Но очевидно, что плохо разработанная программа, записанная по правилам структурного программирования, так и останется плохой программой. В этом плане переход от языка BASIC к таким языкам структурного программирования, как Pascal и Ada, мало что даст.

    Подводя итог сказанному, приведем некоторые рекомендации, которые, как мы надеемся, при осознанном и неформальном их использовании помогут Вам выработать свой стиль программирования. Большинство рекомендаций имеют достаточно общий характер и могут быть использованы при создании программ на многих языках программирования. Эти рекомендации не являются исчерпывающими. Много подобных советов читатель может почерпнуть из книг по разработке программ и структурному программированию [3-6].

  1.    


        Это поможет лучше понять задачу и найти наиболее естественный путь ее решения.

       

  2. Максимально используйте "изобразительные" возможности языка программирования. В частности,


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

       

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

       

  4. Не пытайтесь сразу написать эффективную программу. Это может привести к противоположному результату. Помните о следующем эмпирическом факте: 75% времени выполнения программы приходится на 25% ее текста. Лишь тестирование первого варианта Вашей программы сможет выявить эти 25%, которые действительно требуют тщательной проработки. Экономия на мелочах нередко приводит к проигрышу в целом.

       

  5.    


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

       

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

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

       

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


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

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

       

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

       

  9. Чтобы снизить погрешность результатов при выполнении вычислений с действительными числами, следует:
    • избегать вычитания близких чисел;
    • избегать деления больших по модулю чисел на малые числа, особенно если последние имеют невысокую точность;
    • сложение (вычитание) длинной последовательности чисел начинать с меньших чисел;
    • стремиться уменьшить число операций;
    • использовать алгоритмы, для которых известны оценки ошибок;
    • не использовать сравнение на равенство действительных чисел в операторе If.

       

  10. Программисты в своих программах не всегда явно присваивают начальные значения используемым переменным, полагаясь на то, что интерпретатор присвоит им вполне определенные значения. Это может привести к неверной работе программы. Чтобы избежать такой ошибки, следует явным образом присваивать начальные значения всем используемым в программе переменным. Такие "сокращения" предназначены для дилетантов или тех, кто программирует от случая к случаю, но не программистов-профессионалов.


       

  11. При использовании в записи идентификатора цифр помещайте их в конце идентификатора, так как цифры 0, 1, 2, 3, 4, 5 в середине идентификатора можно воспринять как буквы O, I, Z, З, Ч, S.

        Никогда не используйте переменную более чем для одной цели. Распространенный прием экономии лишнего слова памяти или лишнего оператора для описания типа состоит в повторном использовании переменной в различных условиях. Программист вполне может решить: "Я закончил работать с TIME для расчетов времени, поэтому теперь буду использовать эту переменную как промежуточную при вычислении даты". Такая практика увеличивает шансы внесения ошибки при модификации программы.

       

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

       

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

       

  14. Предусматривайте "фразу" ELSE для каждой "фразы" THEN. В операторе условного перехода должно быть поровну "фраз" THEN и ELSE. Даже если не нужно ничего делать в случае "фразы" ELSE, следует предусмотреть пустой оператор. Это подскажет читателю, что случай "пустого" ELSE также рассматривается, и поможет понять последовательность действий.

       

  15. Не пишите изменяющих себя программ.

       

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


       

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

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

        Объем вычислений можно уменьшить различными методами. Прежде всего необходимо исключить лишние вычисления. Следует убедиться, что одно и то же значение не вычисляется в программе многократно. Вот что иногда можно увидеть в программе:

        .   .   .   .   .
        For i:=1 To N Do
          For j:=1 To 1000 Do
          Begin
             k:=i+3;
             .   .   .   .
          End;
        .   .   .   .   .
    

        В этой программе выражение k:=i+3 вычисляется 1000 раз для каждого из N значений параметра цикла i. Чтобы устранить ошибку, этот оператор следует записать перед оператором For j:=1 To 1000 Do (произвести "чистку" цикла).

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

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


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

    • "Если программа неверна, то ее быстродействие не имеет значения. Убедитесь в ее правильности прежде, чем Вы начнете ее "улучшать".
    • Сохраняйте программу ясной и понятной, не пытайтесь повысить ее быстродействие в процессе кодирования. Преждевременная оптимизация - корень всех бед.
    • Не стремитесь к оптимизации каждого простого вычисления. Пусть транслятор делает это за Вас.
    • Беспокойтесь об алгоритме, а не о деталях программы. Помните, что структура данных может существенно повлиять на то, как будет реализован алгоритм..." [7].

    Вейнберг [8] рассказывает забавную историю о новой программе, которая из-за слишком большой сложности оказалась совершенно ненадежной. Был вызван новый программист, который нашел лучшее решение и за две недели сделал новую, надежную версию программы. При демонстрации ее работы он отметил, что его программе требуется 10 секунд на каждую перфокарту. Один из разработчиков первоначального варианта, торжествуя, заявил: "А моей программе требуется только одна секунда на перфокарту". Ответ программиста стал классическим:


   


(1) Jenkins M., Glasgow J. Programming Styles in Nial. IEEE Software. 1986. Vol. 3(1). P.48.
(2) Bobrow D., Stefik M. Perspectives on Artificial Intelligence Programming. Science. 1986. Vol. 231. P. 951.
(3) Вирт Н. Систематическое программирование. Введение. - М.: Мир, 1977.
(4) Дал У., Дейкстра Э., Хоор К. Структурное программирование. - М.: Мир, 1975.
(5) Иордан Э. Структурное программирование и конструирование программ. - М.: Мир, 1979.
(6) Хьюз Дж., Мичтом Дж. Структурный подход к программированию. - М.: Мир, 1980.
(7) Керниган Б., Плоджер Ф. Элементы стиля программирования. - М.: Радио и связь, 1984.
(8) Weinberg G.M. The Psychology of Computer Programming. New York: Van Nostrand Reinhold, 1971.

    Со следующего шага мы начнем рассматривать методы объектно-ориентированного программирования.




Предыдущий шаг Содержание Следующий шаг