На этом шаге мы рассмотрим понятие стиля программирования и приведем несколько рекомендаций по его улучшению.
Трудно дать исчерпывающее определение понятия "стиль программирования". Попытаемся охарактеризовать его, рассмотрев с различных точек зрения. Главный тезис состоит в том, что стиль программирования - это стиль мышления, проявляющийся в умении переводить алгоритм решения задачи на конкретный язык программирования.
Дженкинс и Глазго считают, что большинство программистов в своей работе используют один язык программирования и программируют в условиях (среде, возможностях), навязанных этим языком, оставляя в стороне альтернативные подходы к цели. Поэтому им трудно увидеть преимущества другого стиля при решении конкретной задачи [1]. Бобров и Стефик выделили пять основных разновидностей стилей программирования (в скобках указаны соответствующие виды абстракций):
Невозможно признать какой-либо стиль программирования наилучшим во всех областях практического применения. Например, для проектирования баз знаний более пригоден стиль, ориентированный на правила, а для вычислительных задач - процедурно-ориентированный.
Не существует совокупности правил написания программ, следуя которым Вы могли бы создавать качественные и не содержащие ошибок программы. Стиль программирования не сводится к хорошему знанию конкретного языка программирования, знанию его возможностей и правил записи программ, хотя он все это и предполагает. Скорее эти знания помогут отличить программу, написанную в хорошем стиле от программы, написанной в плохом стиле. Многие достаточно хорошо знают какой-либо иностранный язык, чтобы без особых затруднений читать специальную или художественную литературу на этом языке. Но лишь немногие могут с такой же легкостью написать на иностранном языке даже небольшую статью. Таким образом, от знания языка до владения языком лежит "дистанция огромного размера". Но даже овладев языком программирования в совершенстве, мы лишь незначительно приблизимся к грамотному стилю программирования.
К сожалению, взгляд на стиль программирования как на стиль мышления еще не всеми и не до конца осознан. Очень часто стиль программирования сводится к технологии программирования. За последние годы мы пережили несколько вспышек увлечения модными методологиями и технологиями программирования, такими например, как структурное программирование. Но в главном все они ориентируются на внешние факторы, характеризующие программу. Кстати сказать, жертвой структурного программирования чуть было не стали FORTRAN и BASIC с их неструктурными операторами. Но очевидно, что плохо разработанная программа, записанная по правилам структурного программирования, так и останется плохой программой. В этом плане переход от языка BASIC к таким языкам структурного программирования, как Pascal и Ada, мало что даст.
Подводя итог сказанному, приведем некоторые рекомендации, которые, как мы надеемся, при осознанном и неформальном их использовании помогут Вам выработать свой стиль программирования. Большинство рекомендаций имеют достаточно общий характер и могут быть использованы при создании программ на многих языках программирования. Эти рекомендации не являются исчерпывающими. Много подобных советов читатель может почерпнуть из книг по разработке программ и структурному программированию [3-6].
Это поможет лучше понять задачу и найти наиболее естественный путь ее решения.
Размещение нескольких операторов на одной физической строке противоречит правилу структурного программирования, требующему сдвигать оператор по строке в соответствии с уровнем его вложенности.
Для этого активно используйте отладочную печать, вставляя соответствующие операторы еще при написании программы. Во всех местах, где происходит ветвление процесса вычислений, необходимо распечатывать данные, определяющие выбор варианта. В местах слияния ветвей решения следует печатать маркер этого места и информацию о том, по какой из ветвей прошло решение.
Печать данных так же, как и представление вводимых данных, должна быть содержательной, отражающей структуру данных и их интерпретацию, а не быть хаотичной грудой цифр. Используйте такие формы отображения, как таблицы и графики.
По мере того, как число таких шаблонов будет увеличиваться, будет расти и Ваше мастерство, умение оценивать все особенности конкретной задачи и пользоваться наиболее адекватными средствами для их отражения. Сказанное не следует понимать как призыв к шаблонному мышлению в программировании.
Скорее наоборот, применение того или иного шаблона требует глубокого предварительного анализа имеющейся ситуации с целью определения, какой именно шаблон можно применить в данной ситуации. И чем больше шаблонов находится в Вашем распоряжении, тем более детальным должен быть этот анализ. Результатом такого анализа может быть рождение новой оригинальной идеи или метода. Таким образом, использование шаблонов освободит Вас от "изобретения велосипеда" в тех ситуациях, когда можно добраться до цели в роскошной гоночной машине, и позволит сосредоточить основное внимание на решении новых для Вас проблем.
Никогда не используйте переменную более чем для одной цели. Распространенный прием экономии лишнего слова памяти или лишнего оператора для описания типа состоит в повторном использовании переменной в различных условиях. Программист вполне может решить: "Я закончил работать с TIME для расчетов времени, поэтому теперь буду использовать эту переменную как промежуточную при вычислении даты". Такая практика увеличивает шансы внесения ошибки при модификации программы.
Если ограничения на память существенны, возможно, придется пожертвовать наглядностью программы и комментариями для экономии памяти. Конечно, программу можно разделить так, чтобы результат получался поэтапно с помощью не одной, а нескольких программ. Можно также уменьшить размеры программы, ограничивая использование повторяющихся операторов или сегментов программы. Для этого может потребоваться объединить модули или сегменты программы, что в определенной степени нарушит модульную структуру программы. Кодирование нескольких операторов в одной строке, удаление необязательных пробелов в операторах, а также исключение комментариев - все эти приемы позволяют сэкономить память, но сделают программу более трудной для понимания, отладки и модернизации.
Объем вычислений можно уменьшить различными методами. Прежде всего необходимо исключить лишние вычисления. Следует убедиться, что одно и то же значение не вычисляется в программе многократно. Вот что иногда можно увидеть в программе:
. . . . . 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 (произвести "чистку" цикла).
На вычисление тригонометрических и экспоненциальных функций затрачивается много машинного времени. Довольно часто мы можем пересмотреть алгоритм и обойтись без этих функций. Вообще сведение всех вычислений только к операциям сложения и вычитания целых чисел существенно увеличивает скорость выполнения программы.
Уменьшение размерности массивов или их исключение также дают выигрыш во времени (например, для движущихся изображений это означает сокращение числа перемещаемых элементов рисунка и увеличение шага каждого перемещения), поскольку адрес элемента двухмерного массива вычисляется дольше, чем одномерного. Кроме того, время доступа к элементу одномерного массива больше, чем к простой переменной.
Тем не менее игнорируйте все предложения по повышению эффективности, пока программа не будет правильной. Худшее, что может быть сделано, - это начать беспокоиться о быстродействии программы до того, как она начнет работать правильно. Быстродействующая, но неправильная программа бесполезна; медленнодействующая, но правильная всегда имеет некоторую ценность, а может оказаться и вполне удовлетворительной.
Вейнберг [8] рассказывает забавную историю о новой программе, которая из-за слишком большой сложности оказалась совершенно ненадежной. Был вызван новый программист, который нашел лучшее решение и за две недели сделал новую, надежную версию программы. При демонстрации ее работы он отметил, что его программе требуется 10 секунд на каждую перфокарту. Один из разработчиков первоначального варианта, торжествуя, заявил: "А моей программе требуется только одна секунда на перфокарту". Ответ программиста стал классическим:
Со следующего шага мы начнем рассматривать методы объектно-ориентированного программирования.