Страница:
этом качестве она очень удобна. Рисунок 15.1 показывает такой граф
подпрограммной структуры.
Рис. 15.1 Граф структуры программы (пример W. V. Wright)
Конечно, такой структурный граф не требует особых усилий по соблюдению
стандартов ANSI для блок-схем. Все эти правила относительно вида
прямоугольников, соединительных линий, нумерации и т.п. нужны только для
понимания подробных блок-схем.
Подробная пошаговая блок-схема является досадным анахронизмом,
пригодным только для новичков в алгоритмическом мышлении. Введенные
Голдштайном и фон Нейманом1 прямоугольники вместе со своим содержимым
служили языком высокого уровня, объединяя непостижимые операторы машинного
языка в осмысленные группы. Как давно понял Иверсон,2 в систематическом
языке высокого уровня группировка уже проведена, и каждый прямоугольник
содержит оператор (рис. 15.2). Поэтому сами прямоугольники являются
утомительным и отнимающим место упражнением в черчении и вполне могут быть
удалены. Тогда остаются только стрелки. Стрелки, связывающие один оператор с
другим, расположенным в следующей строке, излишни, и их можно удалить. Тогда
остаются только GO TO, и если придерживаться хорошей практики
программирования и использовать блочные структуры для минимизации числа GO
TO, таких стрелок окажется немного, но они очень способствуют пониманию.
Вполне можно нарисовать их на листинге и вовсе избавиться от блок-схемы.
В действительности о блок-схемах больше говорят, чем пользуются ими. Я
никогда не видел опытного программиста, который в повседневной деятельности
рисовал бы подробные блок-схемы, прежде чем начать писать программу. Там,
где блок-схемы требуются правилами организации, они почти всегда создаются
задним числом. Многие гордятся использованием специальных программ для
генерации этого "незаменимого инструмента разработки" на основе уже
законченной программы. Думаю, что этот всеобщий опыт не является постыдным и
предосудительным отходом от хорошей практики программирования, признаваться
в котором можно лишь с нервным смешком. Напротив, это результат здравого
рассуждения, дающий нам урок относительно полезности блок-схем.
Апостол Петр сказал о новообращенных язычниках и законе Моисея: "Что же
вы [желаете] возложить на выи учеников иго, которого не могли понести ни
отцы наши, ни мы?" (Деяния апостолов 15:10). То же сказал бы я о
программистах-новичках и устаревшей практике блок-схем.
Самодокументирующиеся программы
Один из основных принципов обработки данных учит, что безрассудно
стараться поддерживать синхронность независимых файлов. Значительно лучше
собрать их в один файл, в котором каждая запись содержит все данные их обоих
файлов, относящиеся к данному ключу.
Тем не менее наша практика документирования программ противоречит
собственным теориям. Обычно мы пытаемся поддерживать программу в виде,
пригодном для ввода в машину, а независимый комплект документации, состоящей
из текста и блок-схем, - в виде, пригодном для чтения человеком.
Результаты этого подтверждают мысль о неразумности поддержки
независимых файлов. Программная документация получается удивительно плохой,
а ее сопровождение - и того хуже. Вносимые в программу изменения не получают
быстрого, точного и обязательного отражения в документе.
Я полагаю, что правильным решением должно быть слияние файлов:
включение документации в исходный текст программы. Это одновременно и
сильный побудительный мотив к должному сопровождению, и гарантия того, что
документация всегда будет под рукой у пользователя. Такие программы называют
самодокументирующимися.
Очевидно, при этом неудобно, хотя и возможно, включать блок-схемы, если
в этом есть необходимость. Но, приняв во внимание анахронизм блок-схем и
использование преимущественно языков высокого уровня, становится возможным
объединить программу с документацией.
Использование исходного кода программы в качестве носителя документации
влечет некоторые ограничения. С другой стороны, непосредственный доступ
читателя документации к каждой строке программы открывает возможность для
новых технологий. Пришло время разработать радикально новые подходы и методы
составления программной документации.
В качестве важнейшей цели мы должны попытаться предельно уменьшить груз
документации - груз, с которым ни мы, ни наши предшественники толком не
справились.
Подход. Первое предложение состоит в том, чтобы разделы программы,
обязанные присутствовать в ней согласно требованиям языка программирования,
содержали как можно больше документации. Соответственно, метки, операторы
объявления и символические имена включают в задачу передать читателю как
можно больше смысла.
Рис. 15.2 Сравнение блок-схемы и соответствующей программы на PL/I
(фрагмент)
Второе предложение - в максимальной мере использовать пространство и
формат, чтобы улучшить читаемость и показать отношения подчиненности и
вложенности.
Третье предложение - включить в программу необходимую текстовую
документацию в виде параграфов комментариев. В большинстве программ
достаточно иметь построчные комментарии. В программах, отвечающих жестким
стандартам организаций на "хорошее документирование", их часто слишком
много. Однако даже в этих программах обычно недостаточно параграфов
комментариев, которые действительно способствуют понятности и обозримости
целого.
Поскольку документация встраивается в используемые программой
структуру, имена и форматы, значительную часть этой работы необходимо
проделать, когда программу только начинают писать. Но именно тогда и нужно
писать документацию. Поскольку подход на основе самодокументирования
сокращает дополнительную работу, меньше препятствий к его осуществлению.
Некоторые приемы. На рисунке 15.3 показана самодокументирующаяся
программа на языке PL/I.3 Числа в кружочках не являются ее частью, а служат
метадокументацией для ссылок при обсуждении.
1. Используйте для каждого запуска свое имя задания и ведите журнал, в
котором учитывайте предмет проверки, время и полученные результаты. Если имя
состоит из мнемоники (здесь QLT) и числового суффикса (здесь 4), то суффикс
можно использовать в качестве номера запуска, связывающего запись в журнале
и листинг. При этом для разных прогонов требуются свои карты задания, но их
можно делать колодами с дублированием постоянных данных.
2. Используйте мнемонические названия программы, включающие
идентификатор версии - в предположении, что будет несколько версий. Здесь
индекс - две младшие цифры года.
3. Включите текстовое описание в качестве комментариев к PROCEDURE.
4. Для документирования алгоритмов ссылайтесь, где можно, на
литературу. Это экономит место, адресует к более полному освещению, чем
можно дать в программе, и дает возможность знающему читателю пропустить
ссылку, оставляя уверенность, что он вас поймет.
5. Покажите связь с алгоритмом, описанным в книге: а) изменения; б)
особенности использования; в) представление данных.
6. Объявите все переменные. Используйте мнемонику. Используйте
комментарии для превращения оператора DECLARE в полноценную легенду.
Обратите внимание, что он уже содержит имена и описания структур, нужно лишь
дополнить его описаниями назначения. Сделав это здесь, вы избежите
отдельного повторения имен и структурных описаний.
7. Поставьте метку в начале инициализации.
8. Поставьте метки перед группами операторов, соответствующие
операторам алгоритма, описанного в книге.
9. Используйте отступы для показа структуры и группирования.
10. Вручную поставьте стрелки, показывающие логический порядок
операторов. Они очень полезны при отладке и внесении изменений. Их можно
поместить на правом поле места для комментариев и сделать частью вводимого в
машину текста.
11. Вставьте строчные комментарии для пояснения всего, что неочевидно.
При использовании изложенных выше приемов они окажутся короче и
малочисленней, чем обычно.
12. Помещайте несколько операторов на одной строке или один оператор на
нескольких строках в соответствии с логической группировкой, а также чтобы
показать связь с описанием алгоритма.
Возражения. Каковы недостатки такого подхода к документированию? Они
существуют, и в прежние времена были существенными, но сейчас становятся
мнимыми.
Рис. 15.3 Самодокументирующаяся программа
Самым серьезным возражением является увеличение размера исходного
текста, который нужно хранить. Поскольку практика все более тяготеет к
хранению исходного кода в активных устройствах, это вызывает растущее
беспокойство. Лично я пишу более краткие комментарии в программах на APL,
которые хранятся на диске, чем в программах на PL/I, которые хранятся на
перфокартах.
Однако одновременно мы движемся к хранению в активных устройствах
текстовых документов, доступ к которым и изменение осуществляется с помощью
компьютеризированных текстовых редакторов. Как указывалось выше, слияние
текста и программы сокращает общее количество хранимых символов.
Аналогичное возражение вызывает аргумент, что самодокументирующиеся
программы требуют больше ввода с клавиатуры. В печатном документе требуется,
по меньшей мере, одно нажатие на клавишу для каждого символа на каждый
черновой экземпляр. В самодокументирующейся программе суммарное количество
символов меньше, и на один символ приходится меньше нажатий на клавиши, так
как черновики не перепечатываются.
А что же блок-схемы и структурные графы? Если используется только
структурный граф самого высокого уровня, он вполне может содержаться в
отдельном документе, поскольку редко подвергается изменениям. Но конечно,
его можно включить в исходный текст программы в качестве комментария, что
будет благоразумно.
В какой мере описанные выше приемы применимы для программ на языке
ассемблера? Я думаю, что базовый подход документирования применим всюду.
Свободным пространством и форматами можно пользоваться с меньшей степенью
свободы, и поэтому они используются не так гибко. Имена и объявления
структур, несомненно, можно использовать. Очень могут помочь макросы.
Интенсивное использование параграфов комментарием является хорошей практикой
в любом языке.
Но подход на основе самодокументирования стимулирован применением
языков высокого уровня и обретает наибольшую мощь и наивысшее оправдание в
языках высокого уровня, используемых в режиме он-лайн, будь то в пакетном
режиме или интерактивно. Как я доказывал, такие языки и системы очень сильно
облегчают жизнь программистов. Поскольку машины сделаны для людей, а не люди
для машин, их использование оправдано как с экономической точки зрения, так
и чисто по- человечески.
Глава 16. Серебряной пули нет - сущность и акциденция в программной
инженнерии Нет ни одного открытия ни в технологии, ни в методах управления,
одно только использование которого обещало бы в течение ближайшего
десятилетия на порядок повысить производительность, надежность, простоту
разработки программного обеспечения.
Резюме1
Создание программного обеспечения всегда включает в себя существенные
задачи - моделирование сложных концептуальных структур, составляющих
абстрактный программный объект, и второстепенные задачи - создание
представлений этих абстрактных объектов с помощью языков программирования и
отображение их в машинные языки с учетом ограничений по памяти и скорости. В
прошлом рост продуктивности программирования по большей части достигался
благодаря устранению искусственных преград, делавших второстепенные задачи
чрезмерно трудными, например, жестких аппаратных ограничений, неудобных
языков программирования, нехватки машинного времени. Какая часть работы
разработчиков программного обеспечения все еще связана со второстепенными, а
не с существенными обстоятельствами? Если она занимает менее 9/10 всех
затрат, то, даже сведя все второстепенные затраты к нулю, мы не получим
роста производительности на порядок величин.
Поэтому, похоже, настало время обратиться к существенным задачам
программирования, связанным с моделированием концептуальных структур большой
сложности. Я предлагаю:
- Использовать массовый рынок, чтобы избежать создания того, что можно
купить.
- Использовать быстрое макетирование как часть запланированных итераций
для установления технических требований к программному обеспечению.
- Органично наращивать программы, добавляя к системам все большую
функциональность по мере их запуска, использования и тестирования.
- Выявлять и растить выдающихся разработчиков концепций нового
поколения.
Введение
Из всех монстров, которыми наполнены кошмары нашего фольклора, самыми
страшными являются оборотни, поскольку нас пугает неожиданное превращение
того, что нам хорошо знакомо, в нечто ужасное. Мы ищем серебряные пули,
которые могли бы волшебным образом уложить оборотней наповал.
Хорошо знакомый программный проект напоминает таких оборотней (по
крайней мере, в представлении менеджеров, не являющихся техническими
специалистами) тем, что, будучи простым и невинным на вид, он может стать
чудищем проваленных графиков работы, раздувшихся бюджетов и неработающих
продуктов.
И мы слышим отчаянные крики с просьбами дать серебряную пулю - нечто,
способное снизить стоимость программных продуктов так же резко, как
снизилась стоимость компьютеров.
Но, вглядываясь в предстоящее десятилетие, мы не видим никакой
серебряной пули. Нет ни одного открытия ни в технологии, ни в методах
управления, одно только использования которых обещало бы хоть на порядок
величин повысить производительность, надежность, простоту. В этой главе мы
попытаемся увидеть, почему это так, исследуя природу задач программирования
и свойства предлагаемых пуль.
Однако скептицизм - это не пессимизм. Хотя мы не видим ошеломляющих
прорывов и действительно считаем их несвойственными природе
программирования, происходит много вселяющих надежды нововведений.
Дисциплинированные и последовательные усилия, направленные на их развитие,
распространение и использование, действительно могут дать рост на порядок
величин. Нет царского пути, но все же путь есть.
Первым шагом к лечению болезней стала замена представлений о демонах и
"соках" в организме теорией бактерий. Сам этот шаг, обещавший надежду,
опроверг все мечты о чудесном исцелении. Он подсказал исследователям, что
прогресс будет осуществляться шажками, с большим трудом, и что постоянное и
неослабное внимание нужно уделять санитарии. То же происходит сегодня с
программной инженерией.
Неизбежны ли трудности? Трудности, вытекающие из сущности
Серебряных пуль не только не видно в настоящее время, но в силу самой
природы программного обеспечения маловероятно, что они вообще будут найдены
- не будет изобретений, способных повлиять на продуктивность создания,
надежность и простоту программного обеспечения так, как электроника,
транзисторы и интегральные схемы - на аппаратное обеспечение компьютеров. Не
следует ожидать, что когда-либо в будущем каждые два года будет происходить
двукратный рост.
Во-первых, следует считать необычным не то, что так медленно происходит
прогресс в программировании, а то, что он так быстро идет в аппаратном
обеспечении компьютеров. Ни одна другая технология за всю историю
цивилизации не имела за 30 лет своего развития роста соотношения
производительность/цена на шесть порядков. Ни одна другая технология не
позволяет выбрать, какой выигрыш предпочесть: улучшить технические
характеристики или снизить затраты. Оба эти выигрыша стали возможны
благодаря переходу производства компьютеров из сборочного производства в
обрабатывающее.
Во-вторых, чтобы посмотреть, какой скорости развития можно ожидать от
программных технологий, полезно изучить имеющиеся в них трудности. Следуя
Аристотелю, я делю их на сущности - трудности, внутренне присущие природе
программного обеспечения, и акциденции - трудности, которые сегодня
сопутствуют производству программного обеспечения, но не являются внутренне
ему присущими.
Акциденции я рассматриваю в следующем параграфе. Сначала рассмотрим
сущность.
Сущностью программного объекта является конструкция, состоящая из
сцепленных вместе концепций: наборов данных, взаимосвязей между элементами
данных, алгоритмов и вызовов функций. Эта сущность является абстрактной в
том отношении, что концептуальная конструкция остается одной и той же при
различных представлениях. Тем не менее она обладает высокой точностью и
большим числом деталей.
Я считаю, что сложность создания программного обеспечения заключается в
задании технических требований, проектировании и проверке этой
концептуальной конструкции, а не в затратах, связанных с ее представлением и
проверкой точности представления. Конечно, мы делаем синтаксические ошибки,
но в большинстве систем они несущественны в сравнении с концептуальными
ошибками.
Верно то, что создание программных систем всегда будет трудным.
Серебряной пули нет по самой природе вещей.
Рассмотрим неотъемлемые свойства этой несократимой сущности современных
программных систем: сложность, согласованность, изменяемость и незримость.
Сложность. Сложность программных объектов более зависит от их размеров,
чем, возможно, для любых других создаваемых человеком конструкций, поскольку
никакие две их части не схожи между собой (по крайней мере, выше уровня
операторов). Если они схожи, то мы объединяем их в одну подпрограмму,
открытую или закрытую. В этом отношении программные системы имеют глубокое
отличие от компьютеров, домов и автомобилей, где повторяющиеся элементы
имеются в изобилии.
Сами цифровые компьютеры сложнее, чем большинство изготавливаемых
людьми вещей. Число их состояний очень велико, поэтому их трудно понимать,
описывать и тестировать. У программных систем число возможных состояний на
порядки величин превышает число состояний компьютеров.
Аналогично, масштабирование программного объекта - это не просто
увеличение в размере тех же самых элементов, это обязательно увеличение
числа различных элементов. В большинстве случаев эти элементы
взаимодействуют между собой неким нелинейным образом, и сложность целого
растет значительно быстрее, чем линейно.
Сложность программ является существенным, а не второстепенным
свойством. Поэтому описания программных объектов, абстрагирующиеся от их
сложности, часто абстрагируются от их сущности. Математика и физические
науки за три столетия достигли больших успехов, создавая упрощенные модели
сложных физических явлений, получая из этих моделей свойства и проверяя их
опытным путем. Это удавалось благодаря тому, что сложности, игнорировавшиеся
в моделях, не были существенными свойствами явлений. И это не действует,
когда сложности являются сущностью.
Многие классические трудности разработки программного обеспечения
проистекают их этой сложности сущности и ее нелинейного роста при увеличении
размера. Сложность служит причиной трудности процесса общения между
участниками бригады разработчиков, что ведет к ошибкам в продукте,
превышению стоимости разработки, затягиванию выполнения графиков работ.
Сложность служит причиной трудности перечисления, а тем более понимания,
всех возможных состояний программы, а отсюда возникает ее ненадежность.
Сложность функций служит причиной трудностей при их вызове, из-за чего
программами трудно пользоваться. Сложность структуры служит причиной
трудностей при развитии программ и добавлении новых функций так, чтобы не
возникали побочные эффекты. Сложность структуры служит источником
невизуализуемых состояний, в которых нарушается система защиты.
Сложность служит причиной не только технических, но и административных
проблем. Из-за сложности трудно осуществлять надзор, а в результате страдает
концептуальная целостность. Трудно найти и держать под контролем все
свободные концы. Обучение и понимание становится колоссальной нагрузкой,
из-за чего текучесть рабочей силы превращается в катастрофу.
Согласованность. Люди, связанные с программированием, не одиноки в
проблемах сложности. Физика имеет дело с объектами чрезвычайной сложности
даже на уровне элементарных частиц. Однако физик работает в твердой
уверенности, что можно найти общие принципы, будь то кварки или общая теория
поля. Эйнштейн неоднократно утверждал, что природа должна иметь простые
объяснения, поскольку Богу не свойственны капризность и произвол.
У разработчика программного обеспечения нет такой утешительной веры.
Сложность, с которой он должен совладать, по большей части является
произвольной, необоснованно вызванной многочисленными человеческими
установлениями и системами, которым должны удовлетворить его интерфейсы.
Системы различаются интерфейсами и меняются во времени не в силу
необходимости, а лишь потому, что были созданы не Богом, а разными людьми.
Во многих случаях программное обеспечение должно согласовываться,
поскольку только что появилось на сцене. В других случаях оно должно
согласовываться, поскольку есть ощущение, что его легче всего согласовать.
Но во всех случаях значительная часть сложности происходит от согласования с
другими интерфейсами, и это невозможно упростить только в результате
перепроектирования программного обеспечения.
Изменяемость. Программные объекты постоянно подвержены изменениям.
Конечно, это относится и к зданиям, автомобилям, компьютерам. Но
произведенные вещи редко подвергаются изменениям после изготовления. Их
заменяют новые модели, или существенные изменения включают в более поздние
серийные экземпляры того же базового проекта. Отзывы у потребителей
автомобилей на практике встречаются весьма редко, а изменения работающих
компьютеров еще реже. То и другое случается значительно реже, чем
модификация работающего программного обеспечения.
Отчасти это происходит потому, что программное обеспечение в системе
воплощает ее назначение, а назначение более всего ощущает влияние изменений.
Отчасти это происходит потому, что программное обеспечение легче изменить:
это чистая мысль, бесконечно податливая. Здания тоже перестраиваются, но
признаваемая всеми высокая стоимость изменений умеряет капризы новаторов.
Все удачные программные продукты подвергаются изменениям. При этом
действуют два процесса. Во-первых, как только обнаруживается польза
программного продукта, начинаются попытки применения его на грани или за
пределами первоначальной области. Требование расширения функций исходит, в
основном, от пользователей, которые удовлетворены основным назначением и
изобретают для него новые применения.
Во-вторых, удачный программный продукт живет дольше обычного срока
существования машины, для которой он первоначально был создан. Приходят если
не новые компьютеры, то новые диски, новые мониторы, новые принтеры, и
программа должна быть согласована с возможностями новых машин.
Короче, программный продукт встроен в культурную матрицу приложений,
пользователей, законов и машин. Все они непрерывно меняются, и их изменения
неизбежно требуют изменения программного продукта.
Незримость. Программный продукт невидим и невизуализуем. Геометрические
абстракции являются мощным инструментом. План здания помогает архитектору и
заказчику оценить пространство, возможности перемещения, виды. Становятся
очевидными противоречия, можно заметить упущения. Масштабные чертежи
механических деталей и объемные модели молекул, будучи абстракциями, служат
той же цели. Геометрическая реальность схватывается в геометрической
абстракции.
Реальность программного обеспечения не встраивается естественным
образом в пространство. Поэтому у него нет готового геометрического
представления подобно тому, как местность представляется картой, кремниевые
микросхемы - диаграммами, компьютеры - схемами соединений. Как только мы
пытаемся графически представить структуру программы, мы обнаруживаем, что
требуется не один, а несколько неориентированных графов, наложенных один на
другой. Несколько графов могут представлять управляющие потоки, потоки
данных, схемы зависимостей, временных последовательностей, соотношений
пространства имен. Обычно они даже не являются плоскими, не то что
иерархическими. На практике одним из способов установления концептуального
контроля над такой структурой является обрезание связей до тех пор, пока
один или несколько графов не станут иерархическими.2
Несмотря на прогресс, достигнутый в ограничении и упрощении структур
программного обеспечения, они остаются невизуализуемыми по своей природе,
подпрограммной структуры.
Рис. 15.1 Граф структуры программы (пример W. V. Wright)
Конечно, такой структурный граф не требует особых усилий по соблюдению
стандартов ANSI для блок-схем. Все эти правила относительно вида
прямоугольников, соединительных линий, нумерации и т.п. нужны только для
понимания подробных блок-схем.
Подробная пошаговая блок-схема является досадным анахронизмом,
пригодным только для новичков в алгоритмическом мышлении. Введенные
Голдштайном и фон Нейманом1 прямоугольники вместе со своим содержимым
служили языком высокого уровня, объединяя непостижимые операторы машинного
языка в осмысленные группы. Как давно понял Иверсон,2 в систематическом
языке высокого уровня группировка уже проведена, и каждый прямоугольник
содержит оператор (рис. 15.2). Поэтому сами прямоугольники являются
утомительным и отнимающим место упражнением в черчении и вполне могут быть
удалены. Тогда остаются только стрелки. Стрелки, связывающие один оператор с
другим, расположенным в следующей строке, излишни, и их можно удалить. Тогда
остаются только GO TO, и если придерживаться хорошей практики
программирования и использовать блочные структуры для минимизации числа GO
TO, таких стрелок окажется немного, но они очень способствуют пониманию.
Вполне можно нарисовать их на листинге и вовсе избавиться от блок-схемы.
В действительности о блок-схемах больше говорят, чем пользуются ими. Я
никогда не видел опытного программиста, который в повседневной деятельности
рисовал бы подробные блок-схемы, прежде чем начать писать программу. Там,
где блок-схемы требуются правилами организации, они почти всегда создаются
задним числом. Многие гордятся использованием специальных программ для
генерации этого "незаменимого инструмента разработки" на основе уже
законченной программы. Думаю, что этот всеобщий опыт не является постыдным и
предосудительным отходом от хорошей практики программирования, признаваться
в котором можно лишь с нервным смешком. Напротив, это результат здравого
рассуждения, дающий нам урок относительно полезности блок-схем.
Апостол Петр сказал о новообращенных язычниках и законе Моисея: "Что же
вы [желаете] возложить на выи учеников иго, которого не могли понести ни
отцы наши, ни мы?" (Деяния апостолов 15:10). То же сказал бы я о
программистах-новичках и устаревшей практике блок-схем.
Самодокументирующиеся программы
Один из основных принципов обработки данных учит, что безрассудно
стараться поддерживать синхронность независимых файлов. Значительно лучше
собрать их в один файл, в котором каждая запись содержит все данные их обоих
файлов, относящиеся к данному ключу.
Тем не менее наша практика документирования программ противоречит
собственным теориям. Обычно мы пытаемся поддерживать программу в виде,
пригодном для ввода в машину, а независимый комплект документации, состоящей
из текста и блок-схем, - в виде, пригодном для чтения человеком.
Результаты этого подтверждают мысль о неразумности поддержки
независимых файлов. Программная документация получается удивительно плохой,
а ее сопровождение - и того хуже. Вносимые в программу изменения не получают
быстрого, точного и обязательного отражения в документе.
Я полагаю, что правильным решением должно быть слияние файлов:
включение документации в исходный текст программы. Это одновременно и
сильный побудительный мотив к должному сопровождению, и гарантия того, что
документация всегда будет под рукой у пользователя. Такие программы называют
самодокументирующимися.
Очевидно, при этом неудобно, хотя и возможно, включать блок-схемы, если
в этом есть необходимость. Но, приняв во внимание анахронизм блок-схем и
использование преимущественно языков высокого уровня, становится возможным
объединить программу с документацией.
Использование исходного кода программы в качестве носителя документации
влечет некоторые ограничения. С другой стороны, непосредственный доступ
читателя документации к каждой строке программы открывает возможность для
новых технологий. Пришло время разработать радикально новые подходы и методы
составления программной документации.
В качестве важнейшей цели мы должны попытаться предельно уменьшить груз
документации - груз, с которым ни мы, ни наши предшественники толком не
справились.
Подход. Первое предложение состоит в том, чтобы разделы программы,
обязанные присутствовать в ней согласно требованиям языка программирования,
содержали как можно больше документации. Соответственно, метки, операторы
объявления и символические имена включают в задачу передать читателю как
можно больше смысла.
Рис. 15.2 Сравнение блок-схемы и соответствующей программы на PL/I
(фрагмент)
Второе предложение - в максимальной мере использовать пространство и
формат, чтобы улучшить читаемость и показать отношения подчиненности и
вложенности.
Третье предложение - включить в программу необходимую текстовую
документацию в виде параграфов комментариев. В большинстве программ
достаточно иметь построчные комментарии. В программах, отвечающих жестким
стандартам организаций на "хорошее документирование", их часто слишком
много. Однако даже в этих программах обычно недостаточно параграфов
комментариев, которые действительно способствуют понятности и обозримости
целого.
Поскольку документация встраивается в используемые программой
структуру, имена и форматы, значительную часть этой работы необходимо
проделать, когда программу только начинают писать. Но именно тогда и нужно
писать документацию. Поскольку подход на основе самодокументирования
сокращает дополнительную работу, меньше препятствий к его осуществлению.
Некоторые приемы. На рисунке 15.3 показана самодокументирующаяся
программа на языке PL/I.3 Числа в кружочках не являются ее частью, а служат
метадокументацией для ссылок при обсуждении.
1. Используйте для каждого запуска свое имя задания и ведите журнал, в
котором учитывайте предмет проверки, время и полученные результаты. Если имя
состоит из мнемоники (здесь QLT) и числового суффикса (здесь 4), то суффикс
можно использовать в качестве номера запуска, связывающего запись в журнале
и листинг. При этом для разных прогонов требуются свои карты задания, но их
можно делать колодами с дублированием постоянных данных.
2. Используйте мнемонические названия программы, включающие
идентификатор версии - в предположении, что будет несколько версий. Здесь
индекс - две младшие цифры года.
3. Включите текстовое описание в качестве комментариев к PROCEDURE.
4. Для документирования алгоритмов ссылайтесь, где можно, на
литературу. Это экономит место, адресует к более полному освещению, чем
можно дать в программе, и дает возможность знающему читателю пропустить
ссылку, оставляя уверенность, что он вас поймет.
5. Покажите связь с алгоритмом, описанным в книге: а) изменения; б)
особенности использования; в) представление данных.
6. Объявите все переменные. Используйте мнемонику. Используйте
комментарии для превращения оператора DECLARE в полноценную легенду.
Обратите внимание, что он уже содержит имена и описания структур, нужно лишь
дополнить его описаниями назначения. Сделав это здесь, вы избежите
отдельного повторения имен и структурных описаний.
7. Поставьте метку в начале инициализации.
8. Поставьте метки перед группами операторов, соответствующие
операторам алгоритма, описанного в книге.
9. Используйте отступы для показа структуры и группирования.
10. Вручную поставьте стрелки, показывающие логический порядок
операторов. Они очень полезны при отладке и внесении изменений. Их можно
поместить на правом поле места для комментариев и сделать частью вводимого в
машину текста.
11. Вставьте строчные комментарии для пояснения всего, что неочевидно.
При использовании изложенных выше приемов они окажутся короче и
малочисленней, чем обычно.
12. Помещайте несколько операторов на одной строке или один оператор на
нескольких строках в соответствии с логической группировкой, а также чтобы
показать связь с описанием алгоритма.
Возражения. Каковы недостатки такого подхода к документированию? Они
существуют, и в прежние времена были существенными, но сейчас становятся
мнимыми.
Рис. 15.3 Самодокументирующаяся программа
Самым серьезным возражением является увеличение размера исходного
текста, который нужно хранить. Поскольку практика все более тяготеет к
хранению исходного кода в активных устройствах, это вызывает растущее
беспокойство. Лично я пишу более краткие комментарии в программах на APL,
которые хранятся на диске, чем в программах на PL/I, которые хранятся на
перфокартах.
Однако одновременно мы движемся к хранению в активных устройствах
текстовых документов, доступ к которым и изменение осуществляется с помощью
компьютеризированных текстовых редакторов. Как указывалось выше, слияние
текста и программы сокращает общее количество хранимых символов.
Аналогичное возражение вызывает аргумент, что самодокументирующиеся
программы требуют больше ввода с клавиатуры. В печатном документе требуется,
по меньшей мере, одно нажатие на клавишу для каждого символа на каждый
черновой экземпляр. В самодокументирующейся программе суммарное количество
символов меньше, и на один символ приходится меньше нажатий на клавиши, так
как черновики не перепечатываются.
А что же блок-схемы и структурные графы? Если используется только
структурный граф самого высокого уровня, он вполне может содержаться в
отдельном документе, поскольку редко подвергается изменениям. Но конечно,
его можно включить в исходный текст программы в качестве комментария, что
будет благоразумно.
В какой мере описанные выше приемы применимы для программ на языке
ассемблера? Я думаю, что базовый подход документирования применим всюду.
Свободным пространством и форматами можно пользоваться с меньшей степенью
свободы, и поэтому они используются не так гибко. Имена и объявления
структур, несомненно, можно использовать. Очень могут помочь макросы.
Интенсивное использование параграфов комментарием является хорошей практикой
в любом языке.
Но подход на основе самодокументирования стимулирован применением
языков высокого уровня и обретает наибольшую мощь и наивысшее оправдание в
языках высокого уровня, используемых в режиме он-лайн, будь то в пакетном
режиме или интерактивно. Как я доказывал, такие языки и системы очень сильно
облегчают жизнь программистов. Поскольку машины сделаны для людей, а не люди
для машин, их использование оправдано как с экономической точки зрения, так
и чисто по- человечески.
Глава 16. Серебряной пули нет - сущность и акциденция в программной
инженнерии Нет ни одного открытия ни в технологии, ни в методах управления,
одно только использование которого обещало бы в течение ближайшего
десятилетия на порядок повысить производительность, надежность, простоту
разработки программного обеспечения.
Резюме1
Создание программного обеспечения всегда включает в себя существенные
задачи - моделирование сложных концептуальных структур, составляющих
абстрактный программный объект, и второстепенные задачи - создание
представлений этих абстрактных объектов с помощью языков программирования и
отображение их в машинные языки с учетом ограничений по памяти и скорости. В
прошлом рост продуктивности программирования по большей части достигался
благодаря устранению искусственных преград, делавших второстепенные задачи
чрезмерно трудными, например, жестких аппаратных ограничений, неудобных
языков программирования, нехватки машинного времени. Какая часть работы
разработчиков программного обеспечения все еще связана со второстепенными, а
не с существенными обстоятельствами? Если она занимает менее 9/10 всех
затрат, то, даже сведя все второстепенные затраты к нулю, мы не получим
роста производительности на порядок величин.
Поэтому, похоже, настало время обратиться к существенным задачам
программирования, связанным с моделированием концептуальных структур большой
сложности. Я предлагаю:
- Использовать массовый рынок, чтобы избежать создания того, что можно
купить.
- Использовать быстрое макетирование как часть запланированных итераций
для установления технических требований к программному обеспечению.
- Органично наращивать программы, добавляя к системам все большую
функциональность по мере их запуска, использования и тестирования.
- Выявлять и растить выдающихся разработчиков концепций нового
поколения.
Введение
Из всех монстров, которыми наполнены кошмары нашего фольклора, самыми
страшными являются оборотни, поскольку нас пугает неожиданное превращение
того, что нам хорошо знакомо, в нечто ужасное. Мы ищем серебряные пули,
которые могли бы волшебным образом уложить оборотней наповал.
Хорошо знакомый программный проект напоминает таких оборотней (по
крайней мере, в представлении менеджеров, не являющихся техническими
специалистами) тем, что, будучи простым и невинным на вид, он может стать
чудищем проваленных графиков работы, раздувшихся бюджетов и неработающих
продуктов.
И мы слышим отчаянные крики с просьбами дать серебряную пулю - нечто,
способное снизить стоимость программных продуктов так же резко, как
снизилась стоимость компьютеров.
Но, вглядываясь в предстоящее десятилетие, мы не видим никакой
серебряной пули. Нет ни одного открытия ни в технологии, ни в методах
управления, одно только использования которых обещало бы хоть на порядок
величин повысить производительность, надежность, простоту. В этой главе мы
попытаемся увидеть, почему это так, исследуя природу задач программирования
и свойства предлагаемых пуль.
Однако скептицизм - это не пессимизм. Хотя мы не видим ошеломляющих
прорывов и действительно считаем их несвойственными природе
программирования, происходит много вселяющих надежды нововведений.
Дисциплинированные и последовательные усилия, направленные на их развитие,
распространение и использование, действительно могут дать рост на порядок
величин. Нет царского пути, но все же путь есть.
Первым шагом к лечению болезней стала замена представлений о демонах и
"соках" в организме теорией бактерий. Сам этот шаг, обещавший надежду,
опроверг все мечты о чудесном исцелении. Он подсказал исследователям, что
прогресс будет осуществляться шажками, с большим трудом, и что постоянное и
неослабное внимание нужно уделять санитарии. То же происходит сегодня с
программной инженерией.
Неизбежны ли трудности? Трудности, вытекающие из сущности
Серебряных пуль не только не видно в настоящее время, но в силу самой
природы программного обеспечения маловероятно, что они вообще будут найдены
- не будет изобретений, способных повлиять на продуктивность создания,
надежность и простоту программного обеспечения так, как электроника,
транзисторы и интегральные схемы - на аппаратное обеспечение компьютеров. Не
следует ожидать, что когда-либо в будущем каждые два года будет происходить
двукратный рост.
Во-первых, следует считать необычным не то, что так медленно происходит
прогресс в программировании, а то, что он так быстро идет в аппаратном
обеспечении компьютеров. Ни одна другая технология за всю историю
цивилизации не имела за 30 лет своего развития роста соотношения
производительность/цена на шесть порядков. Ни одна другая технология не
позволяет выбрать, какой выигрыш предпочесть: улучшить технические
характеристики или снизить затраты. Оба эти выигрыша стали возможны
благодаря переходу производства компьютеров из сборочного производства в
обрабатывающее.
Во-вторых, чтобы посмотреть, какой скорости развития можно ожидать от
программных технологий, полезно изучить имеющиеся в них трудности. Следуя
Аристотелю, я делю их на сущности - трудности, внутренне присущие природе
программного обеспечения, и акциденции - трудности, которые сегодня
сопутствуют производству программного обеспечения, но не являются внутренне
ему присущими.
Акциденции я рассматриваю в следующем параграфе. Сначала рассмотрим
сущность.
Сущностью программного объекта является конструкция, состоящая из
сцепленных вместе концепций: наборов данных, взаимосвязей между элементами
данных, алгоритмов и вызовов функций. Эта сущность является абстрактной в
том отношении, что концептуальная конструкция остается одной и той же при
различных представлениях. Тем не менее она обладает высокой точностью и
большим числом деталей.
Я считаю, что сложность создания программного обеспечения заключается в
задании технических требований, проектировании и проверке этой
концептуальной конструкции, а не в затратах, связанных с ее представлением и
проверкой точности представления. Конечно, мы делаем синтаксические ошибки,
но в большинстве систем они несущественны в сравнении с концептуальными
ошибками.
Верно то, что создание программных систем всегда будет трудным.
Серебряной пули нет по самой природе вещей.
Рассмотрим неотъемлемые свойства этой несократимой сущности современных
программных систем: сложность, согласованность, изменяемость и незримость.
Сложность. Сложность программных объектов более зависит от их размеров,
чем, возможно, для любых других создаваемых человеком конструкций, поскольку
никакие две их части не схожи между собой (по крайней мере, выше уровня
операторов). Если они схожи, то мы объединяем их в одну подпрограмму,
открытую или закрытую. В этом отношении программные системы имеют глубокое
отличие от компьютеров, домов и автомобилей, где повторяющиеся элементы
имеются в изобилии.
Сами цифровые компьютеры сложнее, чем большинство изготавливаемых
людьми вещей. Число их состояний очень велико, поэтому их трудно понимать,
описывать и тестировать. У программных систем число возможных состояний на
порядки величин превышает число состояний компьютеров.
Аналогично, масштабирование программного объекта - это не просто
увеличение в размере тех же самых элементов, это обязательно увеличение
числа различных элементов. В большинстве случаев эти элементы
взаимодействуют между собой неким нелинейным образом, и сложность целого
растет значительно быстрее, чем линейно.
Сложность программ является существенным, а не второстепенным
свойством. Поэтому описания программных объектов, абстрагирующиеся от их
сложности, часто абстрагируются от их сущности. Математика и физические
науки за три столетия достигли больших успехов, создавая упрощенные модели
сложных физических явлений, получая из этих моделей свойства и проверяя их
опытным путем. Это удавалось благодаря тому, что сложности, игнорировавшиеся
в моделях, не были существенными свойствами явлений. И это не действует,
когда сложности являются сущностью.
Многие классические трудности разработки программного обеспечения
проистекают их этой сложности сущности и ее нелинейного роста при увеличении
размера. Сложность служит причиной трудности процесса общения между
участниками бригады разработчиков, что ведет к ошибкам в продукте,
превышению стоимости разработки, затягиванию выполнения графиков работ.
Сложность служит причиной трудности перечисления, а тем более понимания,
всех возможных состояний программы, а отсюда возникает ее ненадежность.
Сложность функций служит причиной трудностей при их вызове, из-за чего
программами трудно пользоваться. Сложность структуры служит причиной
трудностей при развитии программ и добавлении новых функций так, чтобы не
возникали побочные эффекты. Сложность структуры служит источником
невизуализуемых состояний, в которых нарушается система защиты.
Сложность служит причиной не только технических, но и административных
проблем. Из-за сложности трудно осуществлять надзор, а в результате страдает
концептуальная целостность. Трудно найти и держать под контролем все
свободные концы. Обучение и понимание становится колоссальной нагрузкой,
из-за чего текучесть рабочей силы превращается в катастрофу.
Согласованность. Люди, связанные с программированием, не одиноки в
проблемах сложности. Физика имеет дело с объектами чрезвычайной сложности
даже на уровне элементарных частиц. Однако физик работает в твердой
уверенности, что можно найти общие принципы, будь то кварки или общая теория
поля. Эйнштейн неоднократно утверждал, что природа должна иметь простые
объяснения, поскольку Богу не свойственны капризность и произвол.
У разработчика программного обеспечения нет такой утешительной веры.
Сложность, с которой он должен совладать, по большей части является
произвольной, необоснованно вызванной многочисленными человеческими
установлениями и системами, которым должны удовлетворить его интерфейсы.
Системы различаются интерфейсами и меняются во времени не в силу
необходимости, а лишь потому, что были созданы не Богом, а разными людьми.
Во многих случаях программное обеспечение должно согласовываться,
поскольку только что появилось на сцене. В других случаях оно должно
согласовываться, поскольку есть ощущение, что его легче всего согласовать.
Но во всех случаях значительная часть сложности происходит от согласования с
другими интерфейсами, и это невозможно упростить только в результате
перепроектирования программного обеспечения.
Изменяемость. Программные объекты постоянно подвержены изменениям.
Конечно, это относится и к зданиям, автомобилям, компьютерам. Но
произведенные вещи редко подвергаются изменениям после изготовления. Их
заменяют новые модели, или существенные изменения включают в более поздние
серийные экземпляры того же базового проекта. Отзывы у потребителей
автомобилей на практике встречаются весьма редко, а изменения работающих
компьютеров еще реже. То и другое случается значительно реже, чем
модификация работающего программного обеспечения.
Отчасти это происходит потому, что программное обеспечение в системе
воплощает ее назначение, а назначение более всего ощущает влияние изменений.
Отчасти это происходит потому, что программное обеспечение легче изменить:
это чистая мысль, бесконечно податливая. Здания тоже перестраиваются, но
признаваемая всеми высокая стоимость изменений умеряет капризы новаторов.
Все удачные программные продукты подвергаются изменениям. При этом
действуют два процесса. Во-первых, как только обнаруживается польза
программного продукта, начинаются попытки применения его на грани или за
пределами первоначальной области. Требование расширения функций исходит, в
основном, от пользователей, которые удовлетворены основным назначением и
изобретают для него новые применения.
Во-вторых, удачный программный продукт живет дольше обычного срока
существования машины, для которой он первоначально был создан. Приходят если
не новые компьютеры, то новые диски, новые мониторы, новые принтеры, и
программа должна быть согласована с возможностями новых машин.
Короче, программный продукт встроен в культурную матрицу приложений,
пользователей, законов и машин. Все они непрерывно меняются, и их изменения
неизбежно требуют изменения программного продукта.
Незримость. Программный продукт невидим и невизуализуем. Геометрические
абстракции являются мощным инструментом. План здания помогает архитектору и
заказчику оценить пространство, возможности перемещения, виды. Становятся
очевидными противоречия, можно заметить упущения. Масштабные чертежи
механических деталей и объемные модели молекул, будучи абстракциями, служат
той же цели. Геометрическая реальность схватывается в геометрической
абстракции.
Реальность программного обеспечения не встраивается естественным
образом в пространство. Поэтому у него нет готового геометрического
представления подобно тому, как местность представляется картой, кремниевые
микросхемы - диаграммами, компьютеры - схемами соединений. Как только мы
пытаемся графически представить структуру программы, мы обнаруживаем, что
требуется не один, а несколько неориентированных графов, наложенных один на
другой. Несколько графов могут представлять управляющие потоки, потоки
данных, схемы зависимостей, временных последовательностей, соотношений
пространства имен. Обычно они даже не являются плоскими, не то что
иерархическими. На практике одним из способов установления концептуального
контроля над такой структурой является обрезание связей до тех пор, пока
один или несколько графов не станут иерархическими.2
Несмотря на прогресс, достигнутый в ограничении и упрощении структур
программного обеспечения, они остаются невизуализуемыми по своей природе,