Глава 5
Разработка программного обеспечения
Разделив все программное обеспечение на категории, изучим теперь подробнее процесс его разработки. Именно эта область доставляет в настоящее время наибольшие неприятности.
Свойства, присущие всем программам, дают нам возможность глубже проникнуть в более тонкие и более важные характеристики программ. Имеется по крайней мере 12 таких свойств или характеристик. Взгляните на табл. 5.1.
Таблица 5.1
Характеристики программ |
Обычно носят название |
1. Заставляет вычислительную машину выполнить задание |
Функция |
2. Занимает память машины |
Размер |
3. Тратит ресурсы ЦП |
Эффективность |
4. Легкость использования |
Практичность |
5. Легкость восстановления после сбоя |
Устойчивость; восстанавливаемость |
6. Содержит ошибки |
Правильность |
7. Требует времени для создания |
График разработки |
8. Требует людей для создания |
Людские ресурсы на разработку |
9. Требует специального инструментария для разработки |
Материальные ресурсы на разработку |
10. Требует денег для создания |
Стоимость |
11. Модифицируема |
Архитектура, структура |
12. Существует по крайней мере в одной форме, а нужны две |
Документация |
Характеристики программ
Стремление воплотить эти характеристики в программу приводит к конфликтам. В программах, написанных очень быстро (характеристика 7), обычно крайне неэкономно используется память и машинное время (характеристики 2 и 3) по сравнению с программами, которые писались медленнее. Быстро написанные программы часто не выполняют на все 100 % функции, которые они, по предположению, должны были выполнять (1). Программа может печатать почти любую ведомость, однако нам все же приходится держать двух сотрудников для выполнения функций, которые должна была бы (и могла бы) выполнять машина, но которые не были вовремя запрограммированы. Такое случается очень часто.
Если программа печати ведомости нужна нам к 1 июня будущего года, мы можем на этот срок включить дополнительно в группу разработки нескольких программистов, чтобы они помогли сократить время работы. Очевидно, что если на создание программы требуется 100 чел. — мес., то мы могли бы выделить на ее создание 10 программистов, которые будут работать в течение 10 мес. Но сделать эту же работу за один месяц, бросив на нее 100 человек, невозможно.
Укороченные графики могут совершенно разрушить попытки построить стройную, легко модифицируемую структуру (11). Малая стоимость разработки (10) находится в явном противоречии со всеми другими аспектами. (См. табл. 5.2.)
Таблица 5.2. Двенадцать характеристик программы
Фаза разработки |
Требует времени для создания |
График разработки |
Требует людей для создания |
Группа разработки |
Требует специального инструментария для создания |
Средства разработки |
Требует денег для создания |
Стоимость |
Фаза использования |
Заставляет машину выполнить задание |
Функция |
Занимает память машины |
Размер |
Использует ресурсы ЦП |
Эффективность |
Легка для использования |
Практичность |
Легко восстанавливается до рабочего состояния |
Восстанавливаемость |
|
Содержит ошибки |
Правильность |
Фаза сопровождения |
Модифицируема |
Архитектура |
Существует по крайней мере в одной форме, а нужны две |
Документация |
После того как все разработчики представят свои оценки еще не испробованных возможных вариантов, руководство должно принять компромиссное решение.
Давайте посмотрим, какие характеристики существуют в фазе использования.
1. Каждая программа выполняет некоторую функцию, например, она может составлять платежную ведомость.
Здесь мы еще не можем давать численных оценок, но после некоторого наблюдения мы можем определить, действительно ли программа выполняет ту функцию, которая нам нужна.
2. Каждая программа при выполнении занимает некоторое место в памяти. Если памяти у машины мало, нам придется затратить время и людские ресурсы на то, чтобы сжать программу до нужных размеров.
3. При выполнении каждая программа использует некоторые ресурсы машины. Например, сколь быстро напечатает машина нужную нам ведомость по данной программе? Если наш центральный процессор работает медленно, разработчикам придется потрудиться над программой, чтобы выжать из процессора все, что только возможно.
4. Легкость использования. Некоторые программы весьма успешно отражают всякие попытки воспользоваться ими. Они не «дружественны пользователю». Легкость использования — это не случайно возникающее побочное следствие разработки, а качество, требующее тщательного проектирования и разработки специальных требований.
5. Чем больше программа, тем больше вероятность, что в ней имеются ошибки и что самое строгое тестирование не обнаружит их полностью. Большинство этих ошибок может обнаружиться только в необычных комбинациях данных и пользователей.
6. Каждая программа должна сохранять работоспособность. Некоторые из них перезапустить легко, другие сложнее. И опять эта характеристика является объектом тщательного проектирования.
Теперь рассмотрим характеристики фазы разработки.
7. Каждая программа требует на разработку некоторого времени. При разработке больших систем этого времени часто не хватает. И часто именно время является определяющим критерием, доминирующим над всеми другими характеристиками.
8 Каждая программа создается некоторым числом программистов, работающих в течение некоторого времени (чел. — мес.).
9. Для создания любой программы нужны какие-нибудь инструменты. Ими могут быть вычислительные машины, производящие трансляцию с языков высокого уровня и тестирование, перфокарты, магнитные ленты, «пространство», программы для трансляции, тестирования и еще многое другое.
10. Для создания каждой программы необходимы денежные средства.
Теперь обратимся к характеристикам фазы сопровождения.
11. При возникновении новых требований их приходится отражать в программе. Программа, сработанная на скорую руку, труднее поддается модификации, чем программа, проектирование которой велось аккуратно и без спешки. Хорошо ли спроектировано, насколько модульно построено программное обеспечение?
12. Программы так часто хранятся только на магнитных лентах, что упоминание об этом стало уже избитой фразой. Бедняги, которых заставили исправлять в таких программах ошибки и расширять их возможности, должны проводить подлинные археологические раскопки, чтобы понять, что же пытался сделать основной разработчик. Нам необходима четкая документация!
Теперь попытаемся представить все эти характеристики программ на диаграмме, иллюстрирующей жизненный цикл программы (см. рис. 5.1).
Заметьте, что четыре аспекта разработки относятся также и к продолжающейся разработке!
Модифицируемость практически никогда не принимается во внимание, в результате группы сопровождения вынуждены модифицировать «бетонные блоки»! И часто без всякой документации.
Многие программы и не требуется развивать. Многие программы не нужно делать дружественными пользователю. И нам не нужно усложнять разработку таких программ требованиями, выполнение которых необязательно. Но от нашего руководства мы вправе ожидать сознательного отказа от этих требований и четкого обоснования принятого им решения.
Жизнь каждого изделия или инструмента проходит несколько этапов. Все инструменты проходят по крайней мере два этапа — разработку и использование. Некоторые, большая часть, имеет и третью — техническое обслуживание и совершенствование.
Простая деревянная зубочистка, как и молоток, упомянутый в гл.4, проходит всего два этапа. Вы ее делаете, используете и выбрасываете. Никакого ремонта не производится.
Существуют и программы типа «зубочистки», которые я пишу, выполняю, получаю удовлетворяющий меня результат и выбрасываю.
Молотки делают из металла. Мы делаем их и используем. Ремонт им не нужен, но пользуемся мы ими неоднократно.
Существуют программы типа «молотка». Их пишут, их доводят до работающего состояния и исполняют очень много раз. Как и молотки, они не требуют никаких модификаций, изменений или исправлений.
Существуют программы типа «небоскреба», которые, как и те здания, в которых мы работаем, требуют некоторого дополнительного сопровождения. Однажды мы обнаруживаем, что в нашем здании забиты не все гвозди и завернуты не все болты, поэтому нам надо внести некоторые коррективы. Большинство программ относится к типу «небоскребов», так как мы часто добавляем к нашим «зданиям» новые «пристройки».
Мы обратились к этим трем примерам постольку, поскольку они дают нам повод поговорить о качестве программного обеспечения Качество зубочисток необходимо только при их использовании. Для программ типа «зубочисток» нам не приходится заботиться о компоновке — до тех пор пока они работают на нас, — нам не нужно думать об их документировании, раз мы собираемся использовать их только однажды, а затем выбросить.
Качество молотка нас заботит больше, поскольку нам приходится использовать его продолжительное время. Не так уж много молотков ломается при первом же ударе, но плохо изготовленный молоток сломается гораздо быстрее, чем молоток, изготовленный из более хорошего материала и более старательно.
Так же обстоит дело и с программами типа «молотка». Чем дольше мы собираемся ими пользоваться, тем больше старания и затрат (имеются в виду прежде всего деньги) мы вкладываем в их изготовление, надеясь, что эти вложения оправдаются на этапе использования программ типа «молотка».
Заметьте, однако, что для этих программ мы не предусматриваем этапа модификаций (обслуживания). Поэтому документация необходима только для использования программ. До тех пор пока у нас есть хотя бы одна копия программы, написанная в форме, подходящей для ввода в машину, никакая документация на эту программу нам не нужна. Если программа правильна и не требует никаких изменений, все, что нам будет нужно, это инструкции, обращенные к пользователям.
Обращаясь теперь к программам типа «небоскребов», сразу же заметим, что если нам нужно добавить к небоскребу дополнительные помещения, то для оценки, планирования и выполнения всех работ пригодятся любые сведения о проложенных в здании водопроводных трубах, балках, электрических соединениях, проводах и еще о массе различных вещей. Нам будет нужна не пользовательская документация, а сведения о деталях конструкции, которые могут облегчить нам процесс перестройки.
Единственной причиной, по которой нам необходима документация на нашу программу типа «небоскреба», является то, что нам известно о предстоящих в будущем изменениях, а документация сильно облегчает этот процесс.
Это наиболее интересное утверждение; давайте обсудим его подробнее.
Если сформулировать это утверждение в негативной форме, то оно будет выглядеть так: если мы абсолютно уверены в том, что нам никогда не потребуется ни изменять, ни исправлять работающую для нас программу, то в виде, пригодном для чтения ее человеком, она нам никогда не понадобится.
Если бы мы были обладателями такой программы (а в настоящее время в мире достаточное число таких обладателей), мы бы просто загрузили эту программу в память машины, выполнили бы ее, получили бы результаты и были бы ими довольны.
Конечно, временами мы бы волновались из-за того, что в программе могут быть скрытые дефекты или ошибки, или из-за того, что окружение программы меняется, и нам необходимо либо разбираться в структуре программы, либо писать полностью новую программу. Но, если все эти ужасы минуют нас стороной, пока мы находимся «у руля», мы можем сказать, что абсолютно удовлетворены работой нашей программы. Она выполнила свое дело, а мы этим остались довольны.
В настоящее время большинство программ в мире являются программами типа «небоскреба»; в них есть ошибки и недоработки, они нуждаются в модификации, чтобы не отставать от постоянно меняющегося окружения. Следовательно, как и проектировщикам, людям, проводящим модернизацию, необходимы синьки со схемами и документы, помогающие разобраться в причинах ошибок и исправлять их.
В здании, построенном согласно утвердившейся практике, обязательно должны быть предусмотрены вполне определенные коммуникации. Мы можем на это рассчитывать, а это облегчит нашу задачу перестройки.
Так же обстоит дело и с программным обеспечением. Если программисты и проектировщики исходной версии программы следовали «отработанным правилам», наша задача весьма и весьма облегчается. Даже сильнее, чем в строительстве, поскольку мы имеем дело с неосязаемыми вещами.
Процесс разработки программного обеспечения
Существуют различные способы разделения процесса разработки программного обеспечения на этапы. При некоторых из них выделяют большее количество этапов, при других — меньшее. По-видимому, неизбежными являются шесть этапов.
Разработка программного обеспечения:
Определение требований
Проектирование
Написание команд программы
Компоновка
Тестирование
Документирование
Первый род деятельности, определение требований, представляет особую сложность для больших систем типа V, и вскоре мы его рассмотрим весьма подробно.
Проектирование, имеющееся в виду здесь, есть проектирование именно программ, а не той системы в целом, частью которой являются эти программы. Более глубокое изучение этого процесса мы проведем после того, как рассмотрим определение требований.
Третьим пунктом является написание команд, сведение проекта программного обеспечения или просто программы к последовательности машинных команд. Мы будем называть этот процесс программированием.
Компоновка представляет собой комбинирование, связывание отдельных частей программы, написанных разными людьми или группами, в одну большую систему программного обеспечения. Если в нашем распоряжении находятся 700 программистов, создающие отдельные программы, то задача «объединения» этих программ в одну систему может стать просто огромной, требующей много времени, дорогой и чреватой многими ошибками. Мы в дальнейшем увидим, что для компоновки систем из отдельных заранее написанных программ нужно создавать специальные инструментальные средства.
Тестирование, или, как его сейчас часто называют, верификация, — это весьма важный и сложный род деятельности.
Как мы уже знаем, документация особенно важна на этапе продолжающейся разработки.
Полный цикл
Давая поэтапный список процессов как это сделано на рис. 5.2, мы должны понимать, что реально он никогда не бывает таким простым и понятным.
Использование
Очевидно, что только простейшие задачи проходят все шаги без каких-либо итераций, т. е. постоянных возвратов на более ранние этапы процесса. При проектировании кто-то может обнаружить, что следование какому-нибудь требованию может привести к двукратному увеличению стоимости разработки подсистемы. Разработчик должен пересмотреть и переоценить требования. Этот процесс продолжается непрерывно. Группе проектировщиков передается новая информация, проект должен быть пересмотрен. (См. рис. 5.3.)
В конце 60-х гг. в Гейтсбурге в отделении фирмы IBM мы создали специальный курс лекций под названием КУПП — курс управления программным проектом. Этот курс был предназначен для того, чтобы молодые руководители работ лучше понимали, на что обращать особое внимание, как привести проект к оптимальному результату (см. рис. 5.4.). Материал мало менялся с годами, я здесь привожу две диаграммы распределения людских ресурсов, использовавшихся на протяжении 5 лет.
Рис. 5.4 был в конце концов признан неправильным. Для больших проектов проектирование не кончается никогда. Диаграмма была изменена таким образом, чтобы отразить продолжающееся в течение всего этапа разработки проектирование (см. рис. 5.5).
Нам никогда бы не пришло в голову, что деятельность по определению требований, как и проектирование, могла бы продолжаться в течение всего хода работ по разработке. Как показано на рисунках, число занятых людей резко уменьшается после сдачи системы. Такого не случается только с системами типа V. Причина заключается в том, что «закон обязательной даты» — необходимость сдать работу в срок, приводит к временному откладыванию реализации многих обещанных и запланированных функций. Их приходится вводить в действие после «сдачи».
Как мы увидим, распределение времени по различным этапам зависит от множества факторов. Однако размер программной системы, зависящий только от того, какие функции она должна выполнять, является одним из основных определяющих факторов. Время, затрачиваемое на написание программ, сокращается по отношению ко времени и усилиям, затраченным на весь проект в целом, по мере увеличения проекта.
Эти ошибочные диаграммы укомплектования персоналом очень живучи. Диаграмма, изображенная на рис. 5.6, появилась в ведущем журнале отрасли в 1979 г.
Для маленьких программ в хорошо известных уже полностью автоматизированных областях эта диаграмма вполне пригодна. Но она совершенно не подходит большим программным системам и даже системам небольшого размера, относящимся к области управления процессами! Определение требований и проектирование продолжаются гораздо дольше.
Рис. 5.7, созданный Э. Ферентино, полнее соответствует реальной ситуации, или, лучше сказать, ситуации, которая должна возникать при разработке крупномасштабного программного обеспечения.
Еще раз взгляните на модель, использованную нами в отделении федеральных систем фирмы IBM в начале 70-х гг. (рис. 5.5). Процесс проектирования, как мы уже видели, отражен вполне удовлетворительно, но совершенно неправильно представлена роль тестирования. Показано, что тестирование начинается только вместе с кодированием.
Как можно видеть из правильной модели, тестирование должно начинаться вскоре после «первого прохода» процесса определения требований.
«Большой взрыв» и эволюция
Первые из приведенных выше диаграмм отражают подход, называемый теорией «большого взрыва». Этот подход строится на предположениях, что разработчики хорошо знакомы со всеми требованиями, что требования не меняются и что можно спроектировать достаточно эффективную систему, удовлетворяющую этим требованиям. Такое, однако, случается только для простых программ типов I и II, причем только небольших и средних размеров.
Для программ типов III, IV и V дело осложняется тем, что разработчик не вполне понимает все требования. Когда пользователь получает первую версию системы, он говорит: «Ага, посмотрим, что с этим можно сделать», после чего возникает целый поток новых требований! Никто не в силах предугадать, каким образом пользователь будет в действительности применять даваемый ему новый инструмент. Та скорость, с которой разработчик ответит второй версией системы на новые требования, зависит от огромного множества различных обстоятельств, среди которых немаловажную роль играет то, насколько разработчик может контролировать пользователя, иными словами, что же мы создаем — программу как продукцию или программное обеспечение проекта.
Мы уже отмечали, что к моменту первичной сдачи редко завершаются работы по реализации всех необходимых функций. Недоделки устраняются после первого предъявления системы к сдаче.
Необходимо при этом отметить, что кривая, отмечающая количество используемого персонала, после первичной сдачи не должна сразу же резко снижаться! Это и не происходит в действительности! На самом деле строится несколько версий системы, каждая из которых содержит все больше функций, все меньше ошибок, в более поздних версиях все с большей полнотой отражается опыт пользователя.
Такая практика долгосрочного планирования и финансирования разработок редко применяется в больших организациях. «Социология» приемки новых систем, возможно, в некоторой степени объясняет такое положение, но чаще причиной ошибочного выбора методики большого взрыва является полное отсутствие опыта.
«Социология»? Ну конечно, ведь поддержание на высоком уровне количества занятых людей в течение многих лет ведет к таким затратам, что проект может быть отвергнут. Поэтому и выбирают график вида большого взрыва. Для небольших приложений типов I и II такая схема привлечения персонала работает вполне удовлетворительно. В других условиях использование большого взрыва — это чистейшей воды афера, причем совершенно бессмысленная! (См. рис. 5.8.)
Все, что мы говорили до сих пор, ни в малейшей степени не объясняет те огромные затраты людских ресурсов, которые необходимы для создания больших последовательностей команд — программного обеспечения. Теперь подробнее изучим процесс создания и разработки системы из миллиона команд.
Для начала обратимся к конечному результату — что представляет собой программа из миллиона команд? Никто никогда такой программы не видел. Если мы напишем на бумаге миллион строк по 4 команды на 1 см, мы получим рулон бумаги длиной в 250 000 см. Это составляет 2,5 км. бумаги.
Чтобы представить себе миллион команд, представим себе лишь отдельные части программы и их взаимоотношения. Способы представления таких программ будут вкратце описаны на с.155.
Для того чтобы поверить в истинные масштабы затрат на разработку, их иногда необходимо просто увидеть. Десяток, а иногда и несколько десятков людей работают, чтобы выработать требования, установить, что же должна делать система!
Первичное проектирование ведется меньшим количеством людей, но зато написание программы, программирование должно выполняться сотнями, а иногда тысячами программистов. И опять не меньше десятка людей требуется для компоновки, а для тестирования необходимо привлечь несколько десятков, а то и сотен людей. Документирование работ также требует не одного десятка людей. (См. рис. 5.9.)
Определение требований
Этот шаг является важнейшим среди всех шести этапов процесса разработки. Он влияет на все остальные этапы. Увы, это наименее изученный и наименее понятный процесс.
Процесс определения требований разбивается по крайней мере на две части. Во-первых, нужно понять, что нужно сделать, а во-вторых, нужно документировать это. Без этой второй части большой проект останется неуправляемым. Если требования не записаны и не сделаны доступными разработчикам, они вроде бы и не существуют. То, что они существуют в голове какого-нибудь гения, все равно не спасает проект от неожиданной катастрофы.
Требования системного уровня
Определение требований это проблема системного уровня, которая постепенно спускается на уровень программного обеспечения. Если взглянуть на типичную картину большого проекта по управлению процессами типа V, мы увидим что-то похожее на рис. 5.10.
Изменения неизбежны
Требования системного уровня определяют требования для отдельных подсистем. По мере развития работ затраты, возникающие изменения, ограничения, возможные прорывы в подсистемах будут оказывать обратное воздействие на требования системного уровня, делая весь процесс итеративным.
Такие итерации в наибольшей степени отражаются на требованиях к программному обеспечению, поскольку именно оно является последней регулируемой инстанцией, с помощью которой руководство проектом может исправлять всякие несогласованности. Аппаратура — вычислительные машины, радиолокаторы, дисплеи, средства хранения информации, модемы — могут быть исправлены только после нескольких лет напряженного труда. К счастью, программное обеспечение может быть модернизировано в соответствии с новой ситуацией всего за несколько месяцев!
Лос-Анжелесская группа из 500 разработчиков под моим руководством делала систему управления искусственным спутником. Спутник вышел на неправильную, нерегулярную траекторию. Некоторые приборы выполняли не те функции, для которых они были спроектированы, другие же не работали совсем. Ценой огромных усилий мои сотрудники сумели модифицировать программное обеспечение таким образом, чтобы задание все же было выполнено.
И вдруг мы обнаруживаем, что наш заказчик крайне расстроен тем обстоятельством, что нам пришлось выйти из бюджета! Мы терпеливо объясняли ему, что наш перерасход в 3 млн. долларов сэкономил ему несколько лет труда. Кроме того, мы спасли его от необходимости запускать новый спутник, что обошлось бы ему в 50 млн. Нас оправдали, но неохотно!
Программное обеспечение всегда становится последней инстанцией; за его счет исправляют недостатки всех остальных подсистем. Оно получает в наследство чужие промахи и ошибки.
Определение требований для систем типов I и II — достаточно простой процесс, практически всегда он бывает сделан достаточно хорошо. Этого нельзя сказать о больших системах типов III, IV и V. Новизна вычислительной техники комбинируется с трудностью определения требований для больших систем, которые прежде никогда не строились. Люди никогда раньше не применяли автоматизированные системы в этих областях, поэтому они не сразу могут понять все возможности этих новых для них систем.
В дополнение к этой неизвестности действуют еще два фактора. Во-первых, окружение, в котором будет работать система, может измениться еще до того, как система будет введена в действие. Во-вторых, может измениться сама система. Различные ее подсистемы могут оказаться совсем другими, чем предполагалось до ее сдачи.
Таблица 5.3. Изменения в системах реального времени
Не влияют на системы |
Влияют на системы |
Число пользователей |
Спутник |
Стратегия |
Радиолокатор |
Тактика |
Датчики |
Структура организации пользователей |
Дисплеи |
|
Навигационное оборудование |
Режим работы пользователя |
Стартовая площадка |
Приоритеты |
Ракета |
Сети связи |
Интеллектуальные терминалы |
Стратегия принятия решения |
|
Угроза |
|
В действительности лучше было бы задать такой вопрос: «Что же не будет меняться?». Ответ поможет нам определить самое первое требование для больших систем программного обеспечения, которое мы уже проводили на с.103.
Самое первое требование к проектированию больших систем программного обеспечения — предусмотреть возможность будущих изменений!
О том, как этого добиться, мы поговорим в разделе книги, посвященном проектированию программ.
Кто формулирует требования к программному обеспечению?
Каким же образом мы приходим к первой формулировке требований? Я говорю «первой», поскольку мы знаем, что этот процесс будет повторен несколько раз по мере перехода от разработки требований к проектированию и обратно к выработке новых требований.
Кому пристало формулировать требования? Конечно, пользователю, а также проектировщику системы. Формулирование требований следует поручать представителям обеих этих групп. Поодиночке никто из них не выполнит эту работу.
Пользователь знаком с приложениями и большинством нюансов любого сложного проекта. Пользователь должен быть абсолютно уверен, что требования точно и полно отражают стоящую перед ним проблему.
Пользователь не знаком с состоянием технологии и не понимает, что сделать легко, а что сложно. Когда пользователь один формулирует требования, часто оказывается, что возникают технологически наивные формулировки требований, запрашивающие либо слишком много, либо очень мало.
Таблица 5.4. Определение требований пользователем и проектировщиком
|
Формулирование требований |
|
Может сделать |
Пропустит |
Пользователь |
Ясно выразить важные потребности |
Требования к технологии |
Правильно расставить приоритеты |
Потребности инфраструктуры |
Проектировщик |
Определить состояние дел в технологии |
Сортировку интересов пользователя |
Определить полноту сформулированных требований |
Тонкости прикладной области |
Проектировщик обычно не знаком со всеми тонкостями приложений. Если проектировщику позволить формулировать требования самостоятельно, он, вероятнее всего, пропустит некоторые тонкие, но весьма важные функции. Получив такую систему, пользователь может подумать, что ее проектировщики живут в башне из слоновой кости. Разработчик должен сформулировать требования к инфраструктуре, которые обычно непонятны пользователю. Чтобы адекватно отразить все потребности проекта, проектировщик и пользователь должны вырабатывать требования совместно. (См. табл. 5.4.)
Язык документирования требований
Язык, на котором формулируются требования, должен быть понятен пользователю. Пользователь должен участвовать в развитии формулировки требований. Эта формулировка должна стать элементом словаря технического проектирования. Она связана скорее с документами проектирования, а не с документами на требования.
Особая важность требований
Тот факт, что наша работа над программами начинается с установления требований, сильно отражается на всем дальнейшем ходе работ. Если этот процесс выполнен недостаточно аккуратно, недостаточно точно, недостаточно адекватно, то от этого пострадают все остальные части процесса разработки. Последующие усилия могут быть просто героическими, но нужная система уже не будет создана.
Персонал, работающий с созданной системой, будет трактовать ошибки, искажения и неоднозначности как правильные требования, понапрасну тратя усилия не только на то, чтобы их внедрить, но и на то, чтобы затем от них избавиться. Плохие требования подобны кривым зеркалам — они все искажают.
В августе 1977 г. министерство обороны США провело изучение основных автоматизированных систем. Большинство из них были системами связи. Это были системы:
1) TRI — ТАС;
2) LDMX/NAVCOMPARS и AMMC/SRT;
3) SATIN IV;
4) WMMCCS NORAD/COC;
5) WWMCCS ADP — LANTCOM;
6) Телекоммуникационный центр Пентагона;
7) WWMCCS ADP — CCTC;
8) Автоматизированный технологический контроль;
9) CUDIX/NAVMASC.
Изучение показало:
— Во всех системах требования были неустойчивыми и подвергались пересмотру; чем больше была система, тем большие изменения вносились в них.
— В большинстве систем отсутствовал формальный механизм отслеживания и управления процессом выработки требований.
— Некоторые разработчики даже не смогли осознать необходимость обоснования требований.
— В большинстве систем не было отбоя от «списков пожеланий».
Результаты исследования в точности соответствуют моему личному опыту деятельности в коммерческой сфере, охватывающей вычислительную технику.
Опытный строитель, работая на богатого заказчика, может столкнуться с большими затруднениями. У такого заказчика может оказаться «мания» внесения постоянных изменении в проект рабочего кабинета, гостиной, веранды и вообще всех комнат дома. Строитель может быть очень опытным специалистом, знающим, умелым, изобретательным и компетентным в своей области — и все же он не сможет закончить строительство дома в отведенный для этого срок и уложиться в смету. Да и более или менее приличный дом он не построит. Любые изменения, вносимые в какой-либо проект в середине работ, по окончании этих работ обычно выглядят неуместными.
Даже тому, кто остро ощущает их необходимость и вкладывает в свою работу всю душу, с трудом удается четко сформулировать требования. Напомним еще раз:
1) формулируйте требования с максимально возможной строгостью;
2) заранее планируйте изменчивость системы.
Кто является действительным пользователем в любом проекте?
Очень часто разработка систем ведется научно-исследовательскими отделами какой-нибудь организации. В их задачу входит и выяснение того, что необходимо потенциальным пользователям.
Когда в начале 1970-х гг. мы успешно завершили первую рабочую версию новой системы управления воздушными перевозками, мы испытали чувство огромного удовлетворения. Группа определения требований FAA подписала свидетельство о завершении работ. Это была настоящая волокита.
Система была отправлена в Джексонвилл, шт. Флорида, для испытаний в рабочих условиях в ночную смену в Центре управления авиаперевозками. Однако диспетчеры из Джексонвилла отказались пользоваться ею — они заявили, что она «ненадежна».
«Ненадежность» диспетчерской авиаслужбы требует особого внимания. Нам удалось решить проблему исключением из состава системы значительной доли функций, которые ранее были туда включены. Следующий вариант программы, поставленный в Джексонвилле, содержал гораздо меньше системных возможностей. И диспетчерам он понравился. А потом постепенно, очень медленно мы стали добавлять функции, уже запрограммированные и оттестированные нами ранее.
Это служит подтверждением истории, которую я слышал в Хьюстоне. Если вам предложат пуд мороженого, трудно ожидать, что вы сможете проглотить его за один прием. Это не получится.
Такая методика обходится значительно дороже! Последовательное введение функций в действие занимает больше времени. Но у нас нет выбора.
Это другая причина, по которой необходимо отдавать предпочтение эволюционному подходу к разработке систем. Пользователи не могут переварить сразу все функциональные возможности, даже если разработчики и в состоянии их предоставить.
Полученный урок пошел нам впрок. Для дальнейшей работы FAA прикрепило профессиональных диспетчеров к группе определения требований. Несколько лет эти поистине бесценные специалисты делили свое время между основной работой и работами по проекту. Таким образом нам удалось добиться, что группа определения требований работала именно над тем, что было действительно необходимо реальным пользователям.
Чем более крупную систему создают разработчики, тем больше она подвержена опасности стать излишне сложной в использовании. Может получиться и так, что система будет решать давно решенную проблему.
Я принимал личное участие в создании двух крупных систем, на разработку которых были потрачены миллионы долларов и пользователь которых выбрасывал половину предложенных ему функций после того, как они были полностью разработаны. Операторы не могли с ними работать.
Противоречивые требования разных пользователей
Моя группа разработчиков потеряла 2 млн. долларов при разработке автоматизированной системы выпуска двух крупнейших японских газет; позднее за эту работу мы получили японскую медаль Исикавы за достижения в области технологии и промышленности. С одной из газет мы подписали соглашение о том, что беремся автоматизировать весь процесс выпуска от ввода японских иероглифов, редактирования их и разметки страниц с помощью телевизионных дисплеев и до передачи полученных фотоформ прямо на печатные станки. Мы могли бы выполнить эту работу, полностью уложившись в отведенный нам бюджет. Однако японское отделение IBM предложило пользоваться этой системой и другой газете, которая была конкурентом первой. Между этими двумя газетами постоянно возникали разногласия по вопросам, какие функции и как надо исполнять.
Такой противоречивый поток требований сильно повлиял на процесс разработки и проектирования системы. Необходимо было иметь «хребет», фундамент, на котором держались бы различные части программ, выполняющие различные функции, заказанные разными газетами. Такая схема построения и постоянно меняющиеся требования привели к значительному превышению затрат по сравнению с простой автоматизацией одной газеты.
Через несколько лет после описанного случая мы понесли убыток еще на 10 млн. долларов. В это время мы были заняты автоматизацией двух новых нефтеочистительных заводов фирмы «Эксон» — в Эдмонтоне, Канада, и в Антверпене, Бельгия. И опять возникли те же проблемы, сначала мы пытались понять, какие функции нужно вносить в систему, а затем пытались удовлетворить сразу двух разных пользователей. То, что оба этих завода входили в одну компанию «Эксон», еще не означало, что среди них имелось согласие по поводу способов управления процессом очистки. Но опять-таки система работала! И работала хорошо. Просто заранее оказалось сложным оценить весь объем работ с достаточной точностью.
Другой важной причиной, по которой нельзя «замораживать» требования, является то, что пользователи таких систем получают в свое распоряжение совершенно новый инструментарий. На него распространяются те же законы, что и на другие новые орудия труда. По мере роста понимания возможностей нового инструмента пользователи начинают лучше осознавать те функции, которые можно было бы выполнять значительно эффективнее, если бы они были встроены в систему. Предвидеть заранее все функции, а иногда даже хотя бы большинство их не удается.
Один урок, который удается извлечь из всех этих наших усилий, — это разница между «товарными» программами и обеспечением проектов. Проект создается для одного заказчика; товарная продукция должна удовлетворять требованиям многих различных заказчиков. Снова и снова — при работе и на очистительных заводах, и в газетах — мы вынуждены были возвращаться в исходное положение и начинать все сначала, поскольку, когда один заказчик соглашался с предложенными ему методами автоматизации, другой начинал требовать чего-нибудь другого. «Чуть-чуть иначе».
Требования к товарным программам и программному обеспечению проектов
Основным различием между созданием обеспечения как продукции и обеспечения проекта является различие в процессах выработки требований, которым должно удовлетворять программное обеспечение.
Создавая продукцию для продажи, мы должны удовлетворить множество пользователей, поэтому в процессе выработки требований определяется некий компромисс, необходимый для того, чтобы общие затраты не стали чрезмерными. К тому же продукция встречает конкуренцию уже с того момента, как объявляют о ее создании. А если она будет прекрасно работать, но не будет продаваться, какой же от нее будет прок?
Для товарных программ, так же так и для аппаратуры широкого потребления, все время вплоть до того, как разработка перейдет в свои последующие фазы, необходимо иметь значительную по размерам группу широко образованных специалистов по выработке требований.
Изменения, вносимые пользователем
Опытные руководители разработок программ знают, что проектировать систему на основе навязчивых идей одного или целой группы влиятельных пользователей — значит, заранее обрекать систему на недолговечность. Системы, «персонализированные» таким образом, никогда не переживают тех пользователей, для которых они создавались. Эта проблема чаще, чем в других областях, возникает при проектировании военных систем.
Адаптируемость способствует непрекращающимся изменениям
Никто всерьез не осмелится предложить внести крупные изменения в конструкцию моста у Золотых Ворот. И нижние пути на мосту Джорджа Вашингтона появились только благодаря тому, что возможность их появления проектировалась с самого начала.
Конструкции из таких материалов, как кирпич, железо, сталь, строятся в течение многих лет, и перестраивать их не так-то просто. Стальную балку не просто изогнуть, чтобы придать ей новую форму. Никто не перестраивает вычислительную машину, перегруппируя десятки тысяч транзисторов заново и получая тем самым новую архитектуру. Не так обстоит дело с программным обеспечением!
Программное обеспечение существует на бумаге, в памяти машины и нигде больше! Если оно правильно построено, изменять его легко. А нам и нужно, чтобы его можно было легко изменять!
Определение требований — это длительный процесс
Если не поручить определение требований специально выделенным для этого сотрудникам, группа проектирования и реализации создаст «неправильную» систему, которой скорее всего вообще нельзя будет пользоваться.
При обычном ходе событий создается документ, который подписывают представители всех заинтересованных сторон. На него приклеивается этикетка «Требования», а затем он быстро забывается и выходит из употребления. Это архивный хлам, годный только на то, чтобы выискивать в нем ошибки. В одной системе министерства обороны США, состоявшей из программ в 1 млн. строк, в подобный документ в течение 8 лет не вносили изменений. А ведь программы изменялись! Документы с требованиями абсолютно необходимы для правильной организации работ по разработке, в них должны постоянно учитываться происходящие изменения. Для этого нужны деньги, и в течение всего времени жизни проекта им должны заниматься квалифицированные люди.
При работе над многими проектами их руководители даже не видели необходимости в создании групп определения требований. Обычно считалось, что этим могут заниматься группы разработки и/или проектирования. Но они не занимались этим. У них вполне хватало своих дел; они слишком заняты проектированием и разработкой, чтобы заботиться еще и об определении требований; некоторые нужды они просто игнорируют, если последние мешают им уложиться в отведенные сроки и сметы.
Если спросить их, то они конфиденциально ответят, что «знают все требования». В девяти случаях из десяти они не знают ни одного!
Спецификация требований
Английский язык (как, впрочем, и любой другой. — Ред.) полон двусмысленностей и часто приводит к путанице. Мы обнаружили, что он не соответствует задаче создания спецификаций. Прибегнув к помощи американской группы разработки газетных систем, девять американских газет решили автоматизировать свое производство даже раньше, чем это успеют сделать японцы.
Предвидя проблему спецификации требований пользователей, мы потратили значительные усилия и время на создание точного документа размером в 2400 страниц, в котором постарались учесть все. За исключением того, что читали этот документ мы и те, с кем эти требования нам нужно было согласовать, совершенно по-разному. Впрочем с лучшими намерениями. В самом деле, какой редактор может согласиться с редактором другой газеты по вопросу, что считать спецификацией издательского дела? Язык наш недостаточно точен для этого.
С аналогичной проблемой столкнулись мы при создании системы реального времени для военно-морского флота. Когда до срока сдачи оставалось всего несколько месяцев и спецификации требований пользователя уже разрослись в том в полторы тысячи страниц, руководитель разработки сообщил мне, что директивные сроки и финансовые планы будут выполнены, а также, что требования постоянно изменяются. «А что происходит со спецификациями?» — спросил я. «А-а, на них никто не обращает ни малейшего внимания. Они уже устарели на несколько месяцев». И в то же время он старался уложиться в сроки и в смету. Зачем?
Определение требований к окружающей обстановке в фазе использования
До сих пор мы говорили о необходимости определять требования пользователей. Но, кроме того, следует ясно определять требования, относящиеся к окружению в фазе использования. Необходимость удовлетворять этим требованиям может на порядок увеличить затраты на создание программного обеспечения.
Главными факторами фазы использования являются:
1) требования надежности;
2) требования скорости реакции системы;
3) требования реального времени;
4) требования диалогового режима.
Этим требованиям должно удовлетворять как программное, так и аппаратное обеспечение, однако пользователь их замечает, только если они не удовлетворяются. Если все сделано правильно, пользователь никогда этого не замечает.
В этой главе мы обсудим все эти и еще многие другие факторы, из которых складывается обстановка фазы использования и фазы разработки. Мы увидим, почему они имеют столь всеобъемлющий характер.
У разработчика часто возникает возможность навязать пользователю некоторый режим работы! Но эту возможность надо еще заметить и уметь ею воспользоваться! Однажды на моих глазах одна большая система типа III перерастала в систему типа V, потому что никто не сказал пользователям — руководству системы — о том, что систему можно выключать на несколько часов. Это было бы приемлемым условием. Пользователь не простаивал бы без дела, данных для него было вполне достаточно. Система могла не быть безотказной! Но кто-то, опережая требования пользователя — и весьма опрометчиво, — потребовал, чтобы система не имела отказов.
Получить безотказно работающую систему стоит очень дорого, для этого нужно много избыточной аппаратуры и множество логически очень сложных программ.
Управление процессом выработки требований
Требования нельзя просто «собрать» и документировать. Ими нужно управлять! Особенно это необходимо при создании товарных программ. Очень многие товарные программы пропадали — а в них были вложены десятки миллионов долларов — только потому, что руководитель разработки согласился на слишком широкий набор требований. Некоторые называют избыток требований «потерей элегантности». Лучшее — враг хорошего. Обычно оно бывает пагубным. Торговцы всегда будут настаивать на включении n-й функции; руководитель разработки должен отказываться от внесения добавлений к требованиям.
Это же относится и к проектам типов III, IV и V. Все пожелания пользователя даже просто перечислить невозможно (мы предполагаем, что нам удалось найти подлинного пользователя). Это слишком перегрузило бы систему. К тому же пользователь часто и не знает, что необходимо и сколько стоит то, что желательно.
Руководитель разработки программного обеспечения должен назначить кого-нибудь на пост руководителя процессом определения требований, и работа должна идти одновременно в двух направлениях: руководитель определением требований должен свести требования пользователя к действительно необходимым и должен держать пользователя под контролем, не позволяя тому выдвигать нелепые требования.
При создании программного обеспечения проекта пользователь должен сотрудничать с разработчиком. Все связи между разработчиками и пользователями должны быть длительными и интенсивными. Этому способствует их расположение в одном месте! Обязанностью разработчика программного обеспечения является удовлетворение пользователя. Если этого удается добиться, проект будет успешным.
Новые методы определения требований
Для того чтобы сделать первую фазу разработки программного обеспечения более управляемой, разработаны новые методы, хотя их не так уж и много. Аналогично тому, что происходит в области языков программирования и трансляторов, важнейшим принципом, лежащим в основе этих новых методов, является понятие абстракции. Эти методы представляют собой комбинации правил записи и правил ведения работ.
Правила ведения работ помогают достичь нужного уровня абстракции, правила записи необходимы для облегчения взаимодействия и проверок. Эти правила приводят к тому, что люди заняты определением требований в течение длительного периода времени — иногда это даже пугает руководство. Но все окупается.
Для всех методологий характерно следующее:
— Разбиение на уровни абстракции. (Особенно важно для анализа сложных систем.)
— Ограниченное число элементов, приходящихся на один уровень абстракции — очень существенно для успеха, — обычно 7 или меньше.
— Ограниченный контекст: в него включается лишь то, что входит в процесс, все, что в процесс не входит, исключается.
— Включаться должны как данные, так и действия над ними.
Новые методы улучшения процесса определения требований многообещающи и сложны. Обычно они представляют собой комбинации методов документирования и правил ведения работ.
Вот некоторые из этих методов:
SADT |
Технология структурного анализа и проектирования |
IA |
Информационный анализ |
HIPO |
Иерархические схемы ввода, обработки, вывода |
PSL/PSA |
Язык постановки задачи/анализатор постановки задачи |
RSL/REVS |
Язык формулировок требований/системы вычислений и оценок требований |
CARA |
Анализ требований с помощью вычислительных машин |
Большинство методов основано на строгих формальных правилах записи и последовательном приближении к конечному результату.
Эти методы обещают очень многое. Когда-нибудь в будущем мы удивимся, почему же мы раньше обходились без них. Для подробного их описания мало одной странички, мало даже отдельной главы. Для этого нужно писать целые книги, для их понимания необходимо прослушать двухнедельный курс лекций.
Личный опыт
Нам не удалось с первой попытки понять нужды диспетчеров FAA. Группе определения требований нужна была информация, постоянно поступающая от диспетчера, находящегося «на линии». Тогда в Хьюстоне мы правильно поступили, послав наших разработчиков поработать бок о бок с нашими пользователями.
Нашей же хьюстонской группе не сразу удалось понять нужды энергетических компаний. Нам не сразу стали ясны требования к системе автоматической очистки нефти. Первая попытка понять требования японских газетчиков тоже не удалась. Процесс определения требований все еще является камнем преткновения при разработке новых больших программных систем.
Резюме: требования к большим системам программного обеспечения
1. Первым требованием к системе является легкость ее модификации, поскольку требования будут постоянно изменяться. Программы надо заранее делать модифицируемыми; для этого нужно заранее планировать денежные средства.
2. В течение всего времени развития системы за выработку требований должен отвечать один и тот же человек.
3. Требованиями необходимо управлять.
4. Если требования не записаны и не доступны каждому, можно считать, что они не существуют. Пока требования находятся только в голове, они не приносят пользы и не долговечны.
5. Должен быть найден и вовлечен в дело действительный пользователь, а не его заменитель.
6. Из требований нужно удалить все относящееся к личности заказчика.
7. Необходимо действовать по методологии определения требований.
Проектирование
Что такое проектирование?
Это слово одновременно означает и некоторое понятие, и самый процесс. Переходный глагол «проектировать» означает 1) «готовить предварительный набросок или план (для работ, которые нужно в дальнейшем выполнить)», в особенности это относится к составлению схем некоторых структур и форм, например, можно проектировать новый мост; 2) в последнее время этот глагол стали использовать в значении «планировать и моделировать с особым искусством и художественным вкусом».
Область эта трудна именно потому, что она в большой степени связана с искусством. Проектирование подразумевает возможность выбора. Выбирается форма, очертания, последовательность, схема цветовых сочетаний или другие компоненты, и из них составляется некоторое единство.
Проектирование может быть очень хорошим и очень плохим. В архитектуре проектирование имеет функциональное и эстетическое назначение. В смысле функциональности мы можем говорить о том, «хорошо используется» пространство или нет. С точки зрения эстетики речь идет о том, «хорошо ли выглядит» объект или нет. Для оценки функционального проектирования существуют специальные меры или критерии. Во сколько обходится застройка 1 м² площади, или обогрев, или охлаждение? Сколько уже построено «работающих устройств»? Сколько времени потребуется для конструирования? Вообще может использоваться огромное множество разных критериев.
В искусстве же то, что кажется прекрасным одному, может не нравиться другому. Только в тех случаях, когда что-то уж очень хорошо или безобразно, мы можем встретить некоторое подобие единодушия.
Ну а что же мы проектируем при разработке программного обеспечения? Это весьма нетривиальный вопрос. Какие части или куски, формы или цвета, связи и образы можем мы выбирать?
В своих «Заметках о синтезе форм» Кристофер Александер указывает несколько наиболее важных моментов проектирования. Самым интересным является то, что измерять качество проектирования с позитивной точки зрения невозможно, но следует убедиться в отсутствии отрицательных черт, и это дает представление о том, насколько хорошо проект соответствует (его собственные слова) данной ситуации. На идее соответствия автор подробно останавливается, подразумевая под этим применимость проекта к данной ситуации, к данным обстоятельствам.
Александер различает «сознательное» и «бессознательное» проектирование. Бессознательное проектирование длится целые столетия, так что дома и хижины всех местных жителей полностью соответствуют окружению, в котором они используются. Изменения вносятся медленно и постепенно, каждое всесторонне изучается в течение нескольких десятилетий и лишь затем принимается. Проектирование превращается в адаптацию жилища к нуждам жителей.
Сознательное проектирование — это то, чем мы сейчас занимаемся. Со всех сторон на нас обрушиваются различные изменения, мы используем новые инструменты и методы, у нас появляются все новые и новые обязанности. Проектирование становится безотлагательным и специфическим. Качество проектирования оценивается по его соответствию условиям, сравнением его с конкурентными предложениями, если таковые имеются, по его работоспособности.
Программное обеспечение — это подсистема
Чтобы получить необходимую нам систему, мы должны в определенном порядке пройти по разным уровням проектирования: система, подсистема, программное обеспечение, программа и, наконец, рабочая программа — код.
Система как целое состоит из подсистем, и самый выбор этих подсистем уже является проектированием. К примеру, для общенациональной спутниковой системы мы можем остановиться на нескольких различных наборах подсистем.
План № 1 Спутник; система связи космос — Земля; наземная система и т. д.
План № 2 Спутник; микроволновая система; вычислительная система и т. д.
План № 3 Спутник; система связи; система сообщений; управляющая система и т. д.
Этот самый верхний уровень проектирования является самым критическим. Именно он определяет главные черты построения системы, ее работу и обслуживание.
Подсистема программного обеспечения входит в состав подсистемы обработки данных, которая в свою очередь может являться подсистемой одной, двух или даже всех основных подсистем. Конечно, вычислительные машины есть и на спутнике, следовательно, и на спутнике есть программное обеспечение. Вероятнее всего, программное обеспечение есть в каждой большой подсистеме.
Подсистемы обработки данных (ОД) состоят из вычислительных машин и программного обеспечения. Программное обеспечение должно «соответствовать» аппаратуре, оно должно быть спроектировано так, чтобы увеличивать производительность как этой аппаратуры, так и всей системы в целом.
Звучит это просто; однако чаще всего ответственный за аппаратуру ОД мало знает о программном обеспечении, он выбирает машину, только исходя из ее стоимости и скорости работы. Разработка программного обеспечения игнорируется. Я видел, как подобные ошибки допускают даже в тех случаях, когда всеми признавалось, что разработка программного обеспечения будет ключевой во всей схеме разработки системы.
При проектировании систем нужно делать обоснования выбора как аппаратуры, так и программного обеспечения. Проектировщики программного обеспечения должны обязательно поставлять информацию, необходимую для правильного выбора.
Многократное и параллельное проектирование
Существуют десятки и даже сотни методов, которые позволяют производить проектирование параллельно. Иллюстрации этого утверждения приведены на рис. 5.11 — 5.17.
По мере хода работ по проектированию разработчики снова и снова возвращаются к выработанным ранее требованиям, переоценивают их или даже модифицируют в соответствии с тем, как они влияют на усилия проектировщиков.
Одним из наиболее важных результатов проектирования систем является расслоение целой системы на отдельные подсистемы. Выбор подсистем в наибольшей степени определяет природу системы и влияет на ее разработку. Необходимо выработать требования ко всем подсистемам, причем эти требования должны быть согласованы с требованиями верхнего уровня.
Параллельная разработка
На рис. 5.15 показано, что все подсистемы можно разрабатывать параллельно. Очевидно, что разные люди проводят свое собственное проектирование, результаты их труда должны быть проверены и проконтролированы проектировщиком системы (или группой системных инженеров).
Такой процесс все большей и большей детализации верхнего уровня продолжается непрерывно. Начиная с этого места мы будем изучать только одну подсистему. Так как в наибольшей степени нас интересует программное обеспечение, мы будем исследовать подсистему обработки данных (рис. 5.16), в которую входит большая часть всего программного обеспечения. Отметим тут же, что программное обеспечение может в той или иной степени входить в любую подсистему. Итак, подсистема обработки данных должна быть в свою очередь разделена на составные части.
И конечно, каждая из этих подсистем тоже может подразделяться. Чтобы изобразить «окончательное» разбиение на отдельные части, нам нужно построить целое «дерево» проектирования, что и сделано на рис. 5.17.
Очень быстро мы исчерпали все средства и не можем изобразить всю систему на одной диаграмме. Одновременно проводятся работы по проектированию десятков частей! А отслеживание взаимосвязи тех отдельных частей, которые могут взаимодействовать друг с другом, входит в круг обязанностей проектировщика системы. Выбор подсистем во многом базируется на минимизации связей между подсистемами — и проектирование каждой подсистемы должно заключаться в самостоятельном определении средств, при помощи которых можно удовлетворить требованиям к входной информации, поступающей от других подсистем, и к информации, передаваемой из данной подсистемы на вход последующих.
Итерации при проектировании и выработке требований
Итерационный процесс можно проиллюстрировать и так, как это сделано на рис. 5.18. Пунктирными линиями обозначены процессы, которые еще предстоит спроектировать, они основываются на уже определенных областях, отмеченных сплошными линиями. Названия месяцев представляют собой лишь общее описание отрезка времени. При проектировании систем меньшего размера месяцы можно заменять пронумерованными неделями, а для совсем небольших систем можно даже указывать и отдельные дни.
Обратите внимание на полное возобновление работ в марте. По мере более подробной детализации, проводившейся в феврале, проектировщики обнаружили важное упущение или нашли более оптимальное строение системы. Это привело к тому, что им пришлось вернуть процесс в исходное положение и заменить начальную точку А новой точкой В. Очевидно, что такое может произойти со всеми или с большинством подразделов системы. Части 1, 2 или 3, определенные на ранних этапах проектирования, могут использоваться в новых подсистемах В, Р и Л.
Итерации при проектировании идут постоянно. По самому своему характеру работа проектировщика полна сюрпризов. Если бы он мог представить себе весь ход работ в деталях, то это значит, что у него имеется не готовый проект, а лишь иллюстрация к нему. Итерации случаются на всех уровнях, даже на уровне микропроектирования. События и открытия, приводящие к изменениям, не обязательно должны подчиняться общей иерархии, некоторые могут влиять на последующие стадии, а некоторые на стадии, проходящие параллельно. Схема февральских работ показывает, что одновременно происходит детализация и определение четырех различных процессов, значит, эти процессы не существует независимо друг от друга, они чаще всего взаимодействуют между собой Эти взаимодействия нужно исследовать, детализировать и иногда специально проектировать Взаимодействия и детали одной или нескольких работ или функций, возможно, придется видоизменить в соответствии с тем, как проектировщик решил выполнять некоторую другую работу. Может случиться, что после определения работы Ф, нам придется модифицировать П.
Конечно, этот пример не очень точно описывает то, что происходит на самом деле при разработке больших систем. Десятки, сотни работ выполняются одновременно. Вся эта деятельность напоминает работу пчелиного роя, она обязательно должна кем-то координироваться.
Из всего сказанного можно логически вывести такое заключение: утверждение «проектирование завершено на 90 %» — это не факт, а лишь его оценка, поскольку никто в точности не знает, не встретится ли нам какой-нибудь сюрприз, который заставит нас вернуться несколько назад и начать переделку проекта, который мы считали завершенным.
И все же нам не устают повторять, что «проектирование завершено на 90 %».
Проектирование программного обеспечения фазы использования
Давайте на некоторое время предположим, что аппаратура настолько соответствует поставленной перед нами задаче, что меняться больше не будет. Что же в таком случае мы будем проектировать в программном обеспечении?
В первую очередь нам придется спроектировать, т. е. выбрать, главные составные части нашей программы. Нужно ли нам иметь отдельные системные программы? Нужна ли специальная операционная система? Система управления базой данных?
По каким признакам мы можем определить, стоит ли выделять эти подсистемы или включать их в состав прикладных программ? Мы судим об этом по двум вещам: по требованиям, определенным в формулировке требований (имеются в виду требования как к функциональным возможностям, так и к окружению), и по всем 12 характеристикам программ, распределенным по всем фазам жизни программ.
Проектирование должно учитывать все 12 характеристик программ и программного обеспечения и все три фазы полного цикла жизни программы, даже если некоторые характеристики при проектировании придется отбросить!
Для группы десантников, которую ночью направили через болото, любой путь, приводящий к цели, хорош. Это дорога? Она приведет нас туда, куда надо? Да? Тогда это то, что надо! Времени на изучение и поиски более коротких дорог нет. Надо бежать по этой!
Такие методы хороши только для программ типа «зубочисток» или в тех случаях, когда через это болото больше никому не придется переходить.
Если же проектируется долгоживущее программное обеспечение, то на фазе проектирования необходимо затрачивать некоторые усилия на то, чтобы максимально облегчить сопровождение программ в дальнейшем.
Иногда проблемы использования можно не принимать во внимание. Ведь программа может использоваться крайне редко. В некоторых случаях упор приходится делать на фазу разработки, в других случаях особую важность приобретает фаза сопровождения. Более подробно об этом будет сказано чуть позже.
Но если только та машина, которая будет применяться на фазе использования, обладает достаточной мощностью и памятью и мы можем себе позволить некоторые дополнительные расходы, обычно связанные с универсальностью уже готовых системных программ, нам обязательно надо выбирать готовые программы. Следующим нашим шагом должно быть определение наилучших среди всех доступных нам программ. В этом состоит проектирование, это выбор того, какие конкретные программы взять из имеющихся, и это действительно очень ответственное проектирование. При этом формируется система в полном смысле слова. Это расслоение на отдельные части сродни выделению сборочных узлов при проектировании аппаратуры.
Проектировщик соглашается на компромисс между стоимостью и скоростью, между простотой запросов данных и скоростью их поиска, между объемом занимаемой памяти, схемой упаковки и временем ответа, между средствами распределения ресурсов и богатством функций, между средствами управления окружением и выполняемыми функциями, а также между многими другими характеристиками системы.
Кто должен вести проектирование
Заниматься проектированием может не каждый. Дейкстра в статье «Структура мультипрограммной системы „THE“» отмечает два момента:
Попытки избежать разного рода «патологий» отняли гораздо больше сил, чем мы ожидали, причем некоторые наши беды были прямо связаны с нашими изобретениями на первых этапах работ… Если бы мы больше уделяли внимания патологии на ранних стадиях проектирования, мы бы выработали гораздо менее утонченные правила управления работой
И
… по крайней мере в моей стране (имеются в виду Нидерланды) интеллектуальный уровень, необходимый для проектирования систем, чаще всего сильно недооценивается. Этот вид работ чрезвычайно труден, и любые попытки вести проектирование иначе как с помощью наилучших специалистов, обречены либо на провал, либо на весьма умеренный успех за огромную плату
Обратите внимание на слова: «наилучшие специалисты».
Выбирая проектировщика, прежде всего обращайтесь к тем, кто уже ранее выполнял такую работу! К тому, кто уже вел систему от самого начала до самого конца! Постарайтесь договориться с ним. Если в вашем распоряжении нет людей, проектировавших ранее программное обеспечение данного типа, нужно создать «группу проектирования». Последите за ее работой. Из этой группы выделятся один или два человека, которые и будут заниматься проектированием программного обеспечения.
Тот единственный человек, который является подлинным проектировщиком большой системы, обычно весьма умен, спокоен и упорен. Он достаточно спокоен, чтобы быть хорошим руководителем, достаточно умен, чтобы уметь усвоить множество конфликтующих целей, влияний, методов и противоречий между людьми и разобраться в них.
Если проектированием занимается слишком много людей, если при проектировании слишком увлеклись демократией или если проектировщики начинают сменять друг друга — все это очень сильно затрудняет разработку.
Конечная продукция — что же мы создаем?
Зрительно представить себе программное обеспечение очень трудно. Статические представления его весьма бледны, но зачастую это лучшее, что нам удается сделать. Рассмотрим пример программного обеспечения системы наземного контроля проекта «Аполлон», это обеспечение работало в Хьюстоне много лет, и оно поможет нам увидеть все, что мы намеревались создать.
У нас была большая (более 1 млн. команд) программная система, хорошо документированная и оттестированная. Все эти команды были распределены по программам, подпрограммам и модулям. Подразделы строились отдельно и включались во внешние структуры.
Наше программное обеспечение «приняло» несколько форм. У нас имелась распечатка (напечатанный на машине текст) всех команд по порядку. Имелись колода перфокарт и магнитная лента, на которой была записана программа. Ни одно из этих воплощений программы человеку увидеть не легко — даже распечатку. Ни одно из них не давало возможность увидеть структуру или последовательность выполнения команд программы. Чтобы понять идею системы, нам требовались такие схемы, как изображенные на рис. 5.19 и 5.20. Вся программная система была разделена на восемь основных частей, что и показано на рис. 5.19.
Назначение каждой подсистемы совершенно ясно из их наименований. Общее управление системой осуществлялось операционной системой реального времени, которая определяла, какую программу надо исполнять в данное время.
Теперь перейдем к более подробному изучению подсистемы запуска (рис. 5.20). Каждый квадратик обозначает очень большую программу, которая при необходимости будет выполнена. Операционная система определяет, когда наступает такая необходимость. Если мы продолжим разбиение этих блоков программного обеспечения на более мелкие детали, мы получим разветвленное дерево, подобное представленному на рис. 5.21. Каждому квадрату соответствует некоторый акт проектирования. Проектирование здесь заключается в выборе конкретной комбинации функций и распределении их между отдельными вполне определенными программами.
Когда мы смотрим на статическое представление программы, подобное дереву на рис. 5.21, мы должны помнить, что каждый квадрат — это программа, которая может быть выполнена в любое время и в самых разных комбинациях с другими программами.
Для наземного контроля системы «Аполлон» было создано более 400 программных модулей. Но все же «познать» программу мы не могли. Диаграмма давала нам лишь безжизненную иллюстрацию. Работающая программа — это нечто совсем другое.
Это все равно что сравнить художественный фильм с пленкой, на которой снят этот фильм. Увидеть пленку без использования кинопроектора не означает еще увидеть фильм! Мы имеем здесь два совершенно разных явления! Слово «пленка» обозначает понятие носителя, на котором заснят фильм; а слово «фильм» связано с самим результатом — объектами, о которых идет речь в фильме. К счастью, в области программного обеспечения такого рода путаница не возникает.
Составные части и процесс проектирования
В проектировании можно выделить три части. Принятие решения, структуризацию и представление (описание) (см. рис. 5.22).
ПРИНЯТИЕ РЕШЕНИЯ
Собираемся ли мы организовывать поиск последовательным просмотра файла или хотим применить метод двоичного поиска — настраиваясь сразу на середину файла, определяя нужную нам половину, верхнюю или нижнюю, и затем переходя в середину выбранной части, многократно повторяя данный процесс?
Это и есть принятие решения, выбор алгоритма, процесса. Обычно это творческий акт, в результате которого возникает новый метод, особенно в новых, сложных или очень крупномасштабных разработках.
Структура
Выбрав некоторый метод, мы «детализируем» его, превращая в некоторую «структуру». В любой значительной работе к одному и тому же результату могут привести десятки различных структур. Различия в структурах могут быть значительными, результат же будет одинаковым.
Например, выбрав метод двоичного поиска, я могу писать программу либо на Фортране, либо на Коболе. В Коболе затруднено индексирование, а в Фортране очень неудобны манипуляции с данными. Программы на этих языках будут сильно отличаться друг от друга; в результате две разные программы будут осуществлять один и тот же двоичный поиск.
Оценивать структуру можно разными способами. Двумя мерами оценки могут служить модульность и четкость интерфейса.
Описание
Описание, или представление, программы может принимать различные формы. Это могут быть блок-схемы, снабженные некоторыми примечаниями; это могут быть иерархические схемы ввода, обработки, вывода (HIPO), схемы Вернера — Орра и т. д.
Целью описания является объяснение новичкам и продолжателям разработки назначения программы и методов, которыми она решает задачу. Следовательно, оценивать описание нужно по тому, насколько хорошо оно отражает эти сведения. «Хорошо» означает понятно, однозначно и немногословно.
В книге Лоуренса Хэлприна «Творческие процессы в жизни людей» исследуются процессы, с помощью которых люди пытаются проиллюстрировать, документировать в удобочитаемом виде формы в различных областях искусства или связанную с ними деятельность. Слово «партитура» используется автором для обозначения представления, «синтезирования процесса, его развития во времени. Наиболее известны музыкальные партитуры, но я расширил это понятие и включил в него все области человеческой деятельности».
Далее Хэлприн переходит к обсуждению танцев и партитур для них. Вслед за этим он исследует городское планирование, астрономию, архитектуру, игру, посадку на Луну — и десятки других видов деятельности. Он показывает, какой замечательной изобретательностью отличаются люди при создании методов изображения развивающихся процессов.
Он также указывает, что, хотя партитура и сама деятельность неразрывно связаны, они сильно отличаются друг от друга. Скрипач-виртуоз исполняет Брамса намного лучше, чем студент, пользующийся теми же нотами. Разработчики программного обеспечения знают, что всякое описание большой программы статично и что в процессе ее работы может случиться множество вещей, не отраженных в описании! Мы не можем описывать выполняющуюся программу. На с.96 мы уже отмечали тот факт, что при выполнении различных вариантов последовательностей команд их даже невозможно оттестировать. Когда начинает выполняться наша большая система программ, реализуется какой-нибудь один из вариантов.
Многие из новейших «методов проектирования», предлагаемых в настоящее время, имеют чисто описательный характер. Однако это не должно их опорочить. Мы отчаянно нуждаемся в лучших способах обмена сложными идеями.
В большинстве книг, в том числе и в этой, мало пишут о первой части проектирования или создания. Мы просто еще не готовы много говорить об этом. Книги, посвященные подобным вопросам, имеют гораздо более широкую область приложения, чем вычислительная техника и программное обеспечение. Обычно в них идет речь о свободных ассоциациях, или о «мозговом штурме», или об изучении процесса решения реальных проблем.
Последовательный ход процесса проектирования программной системы
Процесс, ход проектирования понимается недостаточно хорошо даже самыми опытными программистами. Многие выполняют его инстинктивно, пропуская некоторые шаги, необходимые для хорошей разработки. Многие выполняют этот процесс очень плохо.
Показанный на рис. 5.23 процесс проектирования начинается после определения требований к системе и развивается, охватывая все более мелкие его этапы.
Функции процесса определения требований рассматривались нами в предыдущем разделе книги. На этой схеме мы снова показываем этот процесс, причем документы, получающиеся в результате, являются первой, исходной точкой проектирования. Заметьте, что мы отделяем действие от продукта, получающегося в результате, помечая и то и другое. Делаем мы это потому, что при разработке программного обеспечения и систем часто путают некоторые из этих терминов.
Второй шаг, проектирование, связан с рассмотрением различных подходов к решению проблемы и с выяснением, могут ли они привести нас к этому решению. Этот шаг часто связан с применением методов проб и ошибок, нередки возвраты к предыдущему шагу. По мере продвижения вперед, нам часто бывает необходимо развить наш проект еще дальше, чтобы проверить, действительно ли мы можем завершить его так, как нам бы этого хотелось. После того как это будет проделано достаточное число раз и с достаточной степенью детализации, мы получим некий подход — и очень часто он оказывается искомым подходом, в большинстве случаев он более или менее работоспособен.
К сожалению, в подавляющем большинстве случаев проектирование на этом и заканчивается, а все дальнейшие работы по созданию системы связаны с воплощением в жизнь только что выбранных вариантов.
Заметим, однако, что в результате второго шага создаются многие тома документов и отчетов. Ведь мы получили какое-то решение. В некоторых случаях кипа документов достигает двух и более метров в высоту.
Может ли это называться проектом? Конечно, но только не вполне законченным, причем, по всей вероятности, он не очень-то нам понятен и его трудно кому-нибудь объяснить! Даже если он правилен!
И надо проделать еще третий шаг, чтобы сделать полученный проект более компактным, четким, легким для понимания и объяснения и, следовательно, для реализации.
Чаще всего третий шаг, связанный с синтезацией и уменьшением объема проекта, пропускается, многие сразу переходят к четвертому шагу — подробному проектированию после завершения шага 2 — нахождения работоспособного решения. Выполнение шага 3 приносит существенную пользу, потеря преимуществ при отказе от него наносит серьезный удар по всей разработке большой программной системы.
Преимущества этого шага, вносящего в проект ясность и краткость, сразу бросятся в глаза после его выполнения. Проект станет легко описываемым и вполне понятным. Лежащие в основе принципы понять просто. Результаты проведенного ранее эвристического (методом проб и ошибок) проектирования (шаг 2) слишком объемисты и неясны. Проект может быть правильным, но пользование им будет связано с большими затратами труда и времени. Обычно он бывает понятен одному или, в более счастливом случае, нескольким лицам. Все остальные участники работ понимают до конца лишь части системы, но не систему как целое.
Переработанный после шага 3 проект имеет то преимущество, что его можно показать всем, тогда проектировщики и программисты подсистем смогут работать над своими частями с большей пользой для общего дела.
На четвертом шаге создается проект верхнего уровня. Положения, сформулированные в нем, служат исходными требованиями для работ по созданию других проектов, более низких уровней.
Повторным применением шага 4 ко всем более низким уровням проекта мы создаем целую серию проектов. Это длится до тех пор, пока не будет создан проект самого нижнего уровня. На каждом уровне создаются как описания требований к более низкому уровню, так и проект модуля; этот процесс продолжается до тех пор, пока не будет достигнут уровень реализации (написания) или уровень тривиальности. Чаще возникает эта последняя ситуация.
Рассмотрим различные варианты третьего шага: шага критической оценки и очистки. На рис. 5.24 направление процесса проектирования указывается стрелкой — слева направо. С левой стороны, в начале проектирования, обозначен процесс создания. Формирующийся при этом проект еще очень расплывчат и находится пока только в голове проектировщика. Небольшой прямоугольник в центре рисунка обозначает очистку, которая следует дальше. Очистка крайне важна. Из проекта исключаются повторы, неоднозначности и тривиальные положения. Внимание сосредоточивается на самых существенных частях решения. В процессе очистки исключаются все случайные, несущественные элементы, выделяются основные принципы, которым надо следовать. Краткое, сжатое определение метода решения облегчает управление всей оставшейся частью работ по разработке программного обеспечения.
Повторяю, что подобная очистка часто не производится. Производить ее трудно, для этого требуется особый талант и очень много труда. Но проводить ее все же надо обязательно.
Расширяющееся пространство на рисунке справа от шага очистки служит для обозначения всевозрастающего объема проектирования и документирования по мере продолжения работ. На базе сформулированных нами фундаментальных основ проектирования мы приступаем к наращиванию уровней детализации, двигаясь прямо к реализации проекта.
Джон Ливингстон Лоувс (1867–1945) в книге «The Road to Xanadu» (1927 г.) пишет о творчестве и приводит примеры великих открытий. Он устанавливает в процессе творчества, открытия, три части, которые называет: ИСТОЧНИК, ВИДЕНИЕ и ВОЛЯ. В источнике находятся все факты, которые ожидают, чтобы их использовали…
Где, действительно, в каждый момент находятся все несчетные известные нам факты и миллионы пережитых нами эпизодов? Где бы это таинственное место ни было… ИСТОЧНИК — это только подходящее обозначение для тайны. И там, поглощенные некими неведомыми глубинами, для всех намерений и целей как бы не существовавшие — они покоились в глубоком сне. Некоторым было суждено дремать неделями, некоторым месяцами, а некоторым целые годы. И вдруг все сразу они пробудились..
Картина пришла в движение и начала разворачиваться помимо моей воли. На мгновение я просто позволил течь потоку образов. Затем я осторожно стал управлять им…
Без ВИДЕНИЯ хаос элементов остается хаосом. А ФОРМА спит вечным сном в просторном хранилище не родившихся замыслов. И в том хаосе лишь творческое ВИДЕНИЕ может различить эту ФОРМУ. Точно так же без содействия ВОЛИ, подчиненной этому ВИДЕНИЮ, воспринятая в сонме других картина не могла бы стать объективной реальностью.
То, что Лоувс называет тремя основными составляющими, очень напоминает описанные нами три части процесса проектирования. Его «воля» хотя и не является полным эквивалентом очистки, но близка ей.
Уровни проектирования
Проектирование автомобилей связано с взаимодействием многих процессов. На рис. 5.25 изображен нисходящий поуровневый метод проектирования автомобиля. На самом верхнем уровне мы пытаемся представить себе автомобиль как целое. На последующих уровнях составляются требования на двигатель, рулевое управление, приборную доску, и, конечно же, на этом уровне может быть еще очень много других составных частей.
В последующем мы начинаем делить на части все эти подсистемы. Перед нами начинает вырисовываться древовидная структура, в которую включается все больше подробностей. Это длится до тех пор, пока мы наконец не достигнем уровня тривиальности, на котором добавление дополнительных подробностей ничего нового не дает. Когда мы начинаем проектировать оконное стекло, или фары, или дверную ручку, мы очень скоро достигаем уровня, на котором все, что мы можем сказать, это — «мы получаем то, что нам было нужно», будучи полностью уверены в том, что все будет сделано так, как нами задумано.
Выбор подсистем, на которые надо делить наш объект, это и есть акт проектирования, он оказывает глубокое влияние на нашу работу по созданию системы и на ее дальнейшее функционирование.
Такое поуровневое проектирование может быть обнаружено практически во всех областях, причем чем более отработана технология, тем более стабильна и надежна та информация, которая передается с одного уровня на другой. Информация, необходимая на любом уровне, обычно весьма понятна, также вполне понятен и отработан механизм представления этой информации (формат, язык и т. д.).
В программном обеспечении ни один из уровневых механизмов не достиг нужной стабильности. Содержимое каждого уровня запутанно, а механизмы передачи информации с уровня на уровень ненадежны, или их вовсе нет. Тем не менее проектировщики нижних уровней часто соглашаются на фрагментарные или невразумительные требования/проекты, потому что для спецификаций не существует никаких стандартов! Более низкие уровни могут легко отойти от нужного подхода, и это с самыми лучшими намерениями.
Отсутствие «технологической инфраструктуры» приводит к тому, что процесс (или система) производства программного обеспечения не застрахован от ошибок. Именно поэтому управление им должно быть гораздо более твердым, чем в отраслях с более отработанной технологией.
Проектирование верхнего уровня
Программа управления, созданная для посадки на Луну, состоит примерно из 400 модулей. В операционной системе IBM — ОС для системы 370 в 1975 г. было более 3000 модулей.
Очевидно, что число различных способов их взаимодействия очень велико. В самом начале процесса отработки взаимосвязей наши возможности имеют поистине астрономические масштабы.
Мы делим функции больших программных систем на все меньшие единицы до тех пор, пока размеры модулей не достигнут порядка 30–50 операторов на языке высокого уровня. После этого начинаем связывать модули друг с другом. Затем их объединяют в подсистемы.
Дойдя до этого места, разработчики вычислительной аппаратуры могут сообразить, что все, о чем мы ведем речь, уже сформулировано в одном из принципов аппаратных систем, который гласит, что чем чище и определеннее связи между различными частями системы, тем легче будет в дальнейшем произвести разделение работ и получить работающие и проверочные подсистемы. Чем более замкнутой является каждая отдельная часть, тем легче будет выполнить всю работу. То же самое мы делаем и с программами, называя результат этого процесса «упрятыванием информации». Скоро мы расскажем об этом подробнее.
При разработке программного обеспечения замкнутость приобретает еще большую важность, поскольку программисту бывает очень легко перешагнуть «границы» программных модулей и забраться в модули, принадлежащие кому-то другому. Нам нужно планировать взаимодействие между компонентами и определять то, что нужно выполнять с помощью подпрограмм.
Связывание и разделение мы проводим на основе расслоения. Все связи между подсистемами должны быть точными и ясными. Это значительно облегчит тестирование, поскольку мы получим возможность сначала протестировать отдельные подсистемы и лишь затем, приступив к их совместному тестированию, сосредоточить внимание на взаимосвязях.
Расслоение
Одной из основ всех сложных предприятий является принцип «разделяй и властвуй», это оказывается верным и для программного обеспечения. Расслоение облегчает задачу повторной сборки построенных блоков после внесения в некоторые из них исправлений Этот принцип очень стар. Он применяется во многих областях человеческой деятельности, в том числе — в технике. Это главнейший принцип, используемый при настоящем техническом подходе к делу.
Выделение макрослоев
Хотя обычно мы не рассматриваем операционную систему или систему управления базой данных в качестве отдельного слоя общей системы, по сути это на самом деле так. С каждой из этих подсистем мы связываем определенные функции, причем эти функции сосредоточиваются в ограниченной части системы.
Временные ограничения
Системы следует проектировать в расчете на пиковые ситуации — ситуации, которые обычно не возникают Если же возможность их возникновения реальна, то именно в этих случаях система особенно необходима (Очень интересно исследовать чистые затраты груда на имитацию пиковых ситуаций при тестировании системы. Мы обратимся к этой теме в разделе, посвященном тестированию). Необходимо идентифицировать, анализировать и изучать пиковые входные сигналы, выходные сообщения пользователям, «поведение» во всех взаимосвязях. Систему надо проектировать так, чтобы она могла управлять именно такой ситуацией.
Проектирование среднего уровня
Результаты проектирования программного обеспечения на среднем уровне могут быть проиллюстрированы на таком примере: как нам разделить на части программу обработки запросов, чтобы работа была максимально эффективной?
В первом варианте решения (см. рис. 5.26) есть модуль «одиночного обслуживания», который строит из всех нуждающихся в нем очередь и «обслуживает» их по одному за раз.
Если очереди станут длинными, то такое решение приведет к задержкам, т. е. одновременно будет поступать много запросов. Второй вариант решения заключается в том, что в памяти машины одновременно находится несколько копий одного модуля, так что одновременно можно обрабатывать несколько очередей. Однако на это затрачивается много памяти.
Третий вариант предлагает одну программу обработки очередей, которая одновременно может работать сразу с несколькими очередями. Эта программа является повторно входимой. Она может одновременно выполняться для нескольких пользователей. Вариант, который будет выбран группой проектировщиков, окажет значительное влияние на производительность системы.
Выделение микрослоев
Внутри модуля, служащего для управления линиями связи, можно иметь около 100 микромодулей размером около 30 операторов на языке высокого уровня или 30 модулей размером в 100 операторов. Каждый микромодуль должен быть максимально изолирован от других. Под этим мы, в частности, подразумеваем следующее:
1. Ни один модуль не зависит от других, кроме как в смысле последовательности исполнения; каждый модуль должен быть обеспечен всеми данными, кроме тех, которые нужно вводить с внешних устройств.
2. Все модули должны иметь минимальные размеры, лучше всего около 30 операторов на языке высокого уровня. Этим достигается микромодулярность.
3. Каждый модуль должен выполнять одну и только одну функцию.
Ключ к созданию легко изменяемого программного обеспечения заключается в том, что создавать его надо компактными сегментированными разделами. К счастью, для этого у нас теперь есть достаточно стандартные способы. Технические методы описаны в многочисленных публикациях, здесь мы ограничимся перечислением основных моментов.
Проектирование модуля
Мы продолжаем делить программные модули на все меньшие части до тех пор, пока не дойдем до таких частей, которые могут быть «записаны» с помощью команд. Каждую группу команд, которую можно «вызвать» для выполнения, мы называем модулем.
Проект модуля включает в себя:
Размер модуля
Внешние данные
Содержание
Внутреннее строение
Внешние связи
Желательно иметь минимум внешних данных; этим определяется независимость модулей. Модуль, выполняющий единственную, хорошо определенную функцию, строго функционален.
Строго функциональные модули могут быть реализованы методом, позволяющим организовать упрятывание информации. Различные состояния данных, возникающие при выполнении функций модуля, известны только этому модулю, поэтому проектировщики и программисты, составляющие другие модули, не должны знать подробности работы данного модуля. Они будут лишены возможности пользоваться сведениями о том, как мы собирались написать данный модуль. Это удержит их от поспешных решений. Вот так-то!
Упрятывание информации
Принцип упрятывания информации мы проиллюстрируем следующим примером. На языке Бейсик команда печати, в которой за символом @ следует число, например PRINT@ 482, отсчитывает заданное число позиций строка за строкой начиная с самой левой позиции верхней строки и печатает некоторое значение в 482-й позиции. Если я программировал в расчете на использование экрана шириной в 32 позиции, то команда PRINT® 69 вызовет пропуск двух пустых 32-символьных строк и выдачу на экран с отступом на 4 шага вправо. Чтобы напечатать
я должен написать: PRINT@69, N (имя) PRINT® 69, А (адрес)
Но, если используемые нами экраны сменились на 79-символьные, программа станет работать неверно. Я получу в этом случае что-нибудь вроде этого:
Чтобы работать с новыми устройствами, надо менять программу.
Приведенный пример тривиален, но представьте, что нам нужно менять сотни программ, работавших в течение многих лет. Это уже проблема! Многих программистов уже нет; другие заняты в других важнейших делах. Ответ напрашивается сам собой — не будем ставить никаких новых дисплеев! Даже если они более дешевы, более надежны и т. д.!
Обходным путем решения подобной проблемы может быть написание программ, не зависящих от числа позиций на экране. Программист просто следует соглашению, по которому текст начинается: 1) с самого начала или 2) отступя 1/8 часть строки, и т. д.
Системные программы берут указания о том, где надо начать текст, преобразуют их к фактическим значениям в соответствии с фактически подключенным дисплеем, и лишь затем будет выполняться наша программа. Теперь при смене физического дисплея нужно будет изменить только системные программы. Если у нас сотни прикладных программ, выгоды налицо. Возможность внесения изменений планируется заранее, (см. рис. 5.27.)
Почему пользуются упрятыванием информации? Во время разработки на него приходится тратить дополнительные средства — оно удлиняет время проектирования и реализации, оно требует затрат и во время использования, — тратится дополнительная память, не очень много, но все же тратится.
Где же оно помогает? Во время сопровождения модификация таких программ будет проще. Мы построили систему, состоящую из блоков, которые легко вытаскивать и исправлять, причем любое изменение оказывает на другие блоки минимальное воздействие.
Производственные здания можно строить с капитальными внутренними перегородками или с раздвижными стенами. Наши действия при этом будут различными. Различными на первом этапе строительства, различными и в то время, когда нужно будет здания перестраивать.
Если мы знаем, что в программы придется вносить очень небольшое число изменений и что ошибок в программе осталось мало, в этом случае во время разработки, возможно, и не будет особых причин связывать себя дополнительными ограничениями для упрятывания информации.
Приведенный пример упрятывания информации можно относить и к проектированию среднего уровня, и к проектированию нижнего уровня в зависимости от того, сколько информации высвечивается на дисплее.
Если «выдачей» имени и адреса все и ограничивается, я склонен называть это нижним уровнем. Если же нам нужно выдавать на дисплей 40 переменных да еще в восьми различных форматах, это будет уже средний уровень. Опять мы сталкиваемся с положением, когда отсутствуют какие-либо метрики.
Проектирование нижнего уровня
Сам акт проектирования — выбор частей, на которые надо делить программу, и их расположение — продолжается вплоть до самых маленьких программ. И для самых маленьких программ необходимо определить требования, провести проектирование, написать команды, тестировать их и, наконец, выполнить всю программу. Мало, очень мало организаций, практикующих на самом нижнем уровне разделение процесса написания команд и проектирования. Очень редко группы проектирования полностью доводят проекты до такой точки, что людям, пишущим команды, не остается делать ничего, кроме тривиальнейшего преобразования. Когда такая точка достигается, человек, пишущий команды, уже не выполняет никакого проектирования и становится кодировщиком. Это определение имеет экономический подтекст.
Программист — это профессионал; кодировщик — это обыкновенный конторский служащий. Такова моя точка зрения; этот вопрос, однако, все еще обсуждается. Министерство труда имеет промежуточное мнение. При чтении циркуляра министерства может создаться впечатление, что все программисты считаются конторскими служащими, которые не обязаны в своей работе проявлять творческую активность. Им, следовательно, нужно платить сверхурочные!
Решением флоридского суда было признано, что три программиста, требовавшие от своего бывшего работодателя сверхурочных выплат за несколько лет, имеют право не только на возмещение убытков, но даже на их возмещение в тройном размере!
Можно себе представить тот хаос, который начнется, если 10 тыс. программистов фирмы IBM вдруг потребуют оплаты нескольких лет сверхурочных работ в трехкратном размере!
Циркуляр министерства труда только на первый взгляд считает программистов не творческими работниками; циркуляр написан весьма неопределенно, он, очевидно, написан человеком, который не понимает, что такое вычислительная машина и что делают программисты!
Ключевое различие между конторскими служащими и творческими работниками, применяемое в министерстве труда, основано на требовании принимать в своей деятельности ответственные решения. Проектирование и состоит в принятии ответственных решений.
Чтобы стать инженером, нужно окончить университет и пройти специальные тесты. Не так обстоит дело в мире программирования. Поскольку акт проектирования очень тяжело поддается описанию, обучать этому делу так же нелегко. Чтобы стать программистом, не обязательно быть выпускником факультета прикладной математики. Но принимать ответственные решения программисты должны, поскольку они должны вести проектирование. Кодировщики этого не делают.
Процесс проектирования даже на самом нижнем уровне, на уровне кодирования, проектирования алгоритма и структуры данных, остается творческим процессом. Если кому-то нужно написать программу в 100 команд, он должен проектировать и всю программу, и ее отдельные части.
Как организовать программу, где расположить константы, где организовывать проверки — после тела функции или перед ним, — все это принимается во внимание на нижнем уровне. Каким образом должен я разделить 3000 операторов языка высокого уровня на отдельные модули? Это и есть принятие ответственного решения.
Межуровневые механизмы и уровень тривиальности
Рассмотренные нами этапы процесса проектирования можно обнаружить во всех отраслях человеческой деятельности — в медицине, авиационной промышленности, мостостроении, строительстве и т. д. И в каждой отрасли имеется свой, специфический для нее момент тривиальности.
Это такой момент, когда становится бесполезным дальнейшее уточнение деталей, поскольку все уже знают, как это делается, и дополнительные подробности лишь вносят путаницу при реализации.
Момент тривиальности обычно зависит от сочетания двух факторов — развитости данной отрасли (какое число людей и в течение какого времени работают в ней) и сложности (сколько сил потребуется, чтобы четко описать все, что нужно описывать). Очевидно, что эти факторы воздействуют друг на друга, причем сложность тормозит развитие отрасли.
Разработка программного обеспечения находится в самом начале пути. Мы уже видели всю ее сложность, к тому же это очень молодая отрасль. Возникла она недавно, занято в ней не так уж много людей.
По мере совершенствования отрасли практические методы и конкретные сведения, передаваемые с одного уровня на другой, изучаются и проверяются опытным путем. Со временем они становятся четко определенными. Если накопленный опыт игнорируется или просто плохо используется, проектировщик нижнего уровня сразу заметит, что спецификация имеет серьезные дефекты. Его реакцией на плохой проект будет комментарий: «Это несерьезно!».
В программировании опыт и практические навыки еще не накоплены. Сталкиваясь с недостаточно подробными, неясными или неточными описаниями, проектировщик нижнего уровня не отвергает их, а с большей энергией принимается за реализацию. Не удивительно, что в результате могут возникать и задержки, и отклонения от целей, и путаница, и неудачи.
Из-за отсутствия стандартов, регламентирующих взаимодействие между уровнями, руководить разработкой программного обеспечения приходится с мучительной точностью. Каждая межуровневая связь, каждый уровень имеют слишком много возможностей для развития в самых случайных направлениях.
Когда мы ведем проектирование в области хорошо нам знакомой, когда мы уверены в успехе, мы доводим наш проект до определенного уровня и останавливаемся, оставляя дальнейшее проектирование менее квалифицированным специалистам. В случае достижения уровня, на котором все оставшиеся работы настолько просты, что ничего плохого уже не может произойти, мы говорим, что достигли уровня тривиальности.
Различия в уровнях тривиальности, которые мы можем наблюдать в разных областях человеческой деятельности, связаны с тем, что некоторые из этих областей достигли большего развития, чем другие, а также с разной степенью подготовленности и компетентности проектировщиков и технического персонала, их способности завершить проект после выхода на тривиальный уровень.
Так, например, при проектировании нового спортивного автомобиля руководитель группы проектировщиков может без всякого сомнения передать работу над структурой двери «деталировщику» Он может быть абсолютно уверен в том, что полученная в результате дверь будет вполне приемлема и работоспособна.
Уверенность руководителя в таком исходе основана не только на том, что в его распоряжении имеются достаточно компетентные «деталировщики». Много лет руководитель занимался тем, что разрабатывал стабильный и развитый механизм определения требований к двери, это позволяет ему не сомневаться в том, что «деталировщик» сможет обеспечить выполнение всех условий и достижение всех целей, указанных в спецификации.
В области разработки программного обеспечения деталировщиков еще мало. Огромная потребность в проектировщиках приводит к тому, что всякий, кто может хоть как-нибудь вести проектирование, быстро переводится на роль главного проектировщика в каком-либо другом проекте, часто это случается чересчур быстро, что приводит в действие принцип Питера. Кроме того, недостаточно разработаны процедуры и правила описания условий и целей. Этот двойной недостаток создает большую путаницу, поскольку проектировщики не осознают, что они не связаны между собой, и бывают очень удивлены и даже взбешены, когда выясняется, что программы, считавшиеся ясными и полностью определенными, возвращаются к ним неузнаваемыми.
Обнаружение только что описанных нами недостатков есть первый шаг, помогающий избежать возникновения таких проблем. Кто предостережен, тот вооружен; он должен проявлять гораздо больше осторожности, вести более тщательный надзор, чем это делалось им ранее.
Связующих механизмов, работающих между разными уровнями проектирования в других отраслях, в программировании не существует. Тем самым проект большой системы программ должен быть значительно более детальным, чем мы обычно считаем необходимым. Когда кто-нибудь просит дать ему взглянуть на проект системы, его надо обязательно спросить: «На каком уровне?».
Однако и в программировании тоже имеется свой уровень тривиальности. Но он лежит значительно ниже, чем в большинстве других технических областей. Тот факт, что связующие механизмы несовершенны, вынуждает проектировщиков на смежных уровнях постоянно следить за тем, насколько точно совпадают их усилия. Если это не так, мы часто становимся свидетелями того, что возникают неработоспособные проекты либо проекты с многочисленными проблемами.
Вариации уровня тривиальности и межуровневых механизмов
Одна и та же работа может иметь разные уровни тривиальности в зависимости от того, кто будет вести ее реализацию — новички или опытные эксперты. То, что приемлемо в одной ситуации, в другой приведет просто к катастрофе. Аналогично, если моя команда уже провела в прошлом достаточное число разработок, я могу чувствовать себя более уверенно и меньше обращать внимание на межуровневые механизмы.
Структурное программирование
Структурное программирование является одним из элементов, относящихся к проектированию. Оно используется для управления связями, определения структур как больших, так и маленьких программ. Существует очень большая путаница по поводу того, что является, а что не является структурным программированием. Многие путают реализацию сверху вниз (см. с.220–221) со структурным программированием. Некоторые смешивают со структурным программированием концепции главного программиста и сквозной контроль.
X. Милс, Р. Лингер и Б. Уитт написали книгу под названием «Структурное программирование», в которой использовали такое определение:
Структуризованная программа — это программа, составленная из фиксированного базового множества первичных программ.
Первичная программа — это простая программа, не имеющая простых подпрограмм, состоящих более чем из одного узла.
Простая программа — это программа, которая:
1) имеет один вход и один выход, и
2) для каждого узла существует путь от входа до выхода, проходящий через этот узел
Это хорошее определение, поскольку оно позволяет определить базовое множество структур в отдельности от программ. (См. рис. 5.28.) Суть дела здесь заключается в том, что если программное обеспечение строится только из первичных и простых программ, то логика и сам ход процесса ее выполнения значительно проясняются благодаря структуризации. Использование таких (готовых) структур дисциплинирует разработчика программ, что в результате приводит к появлению более понятных программ, в которых, следовательно, имеется меньшее число ошибок. К тому же структуризованные программы гораздо легче читать. Сложность программы также «структуризуется».
Полная свобода, которой обладали программисты, подверглась ограничению — это привело к повышению ясности, четкости, к выработке стандартов и напугало многих программистов старой закалки.
Рис. 5.28. Основные конструкции структурного программирования.
То, что структурное программирование идет сверху вниз, это случайность. То, что структурное программирование легко приспосабливается к методам сверху вниз и пошагового уточнения, можно отнести к его дополнительным достоинствам, однако существо структурного программирования не в этом.
Мы включили обсуждения структурного программирования в раздел, посвященный проектированию, а не написанию программ потому, что представление хода выполнения программы в структуризованном виде и передача людям, пишущим программу, информации тоже в структуризованном виде есть не программирование, а проектирование. При написании программы тоже проводится некоторое проектирование, при этом программы нижнего уровня строятся на основе уже знакомых нам базовых структур.
Если же проекты более высоких уровней выполняются раньше проектов более низких уровней и структуризация их не проводилась, то структуризация нижних уровней не приведет ко всем возможным выгодам. Общий ход выполнения программы будет по структуре напоминать блюдо спагетти.
Это может прозвучать здесь, на страницах книги, риторически, но структуризация делает программы гораздо более наглядными и понятными по сравнению с программами, не подвергавшимися этому процессу.
Структурное программирование придало программному обеспечению совершенно новый вид.
Введение в структурное программирование
Автоматизация хранения справочного материала (газетных вырезок) газеты «Нью-Йорк таймс» стала заметной вехой на пути развития программирования, и по праву. Выполненная Т. Бейкером из отделения федеральных систем фирмы IBM под руководством X. Милса, она изменила направление развития программирования. До сих пор она часто упоминается в литературе. Было установлено несколько рекордов производительности. Были доказаны преимущества структурного программирования. Мы настаивали на использовании методов структурного программирования, и последующее обучение им 2600 наших кадровых работников основывалось на эффектных результатах работы с «Нью-Йорк таймс».
События, происшедшие за кулисами этой работы, известны не так широко. Их стоит описать, поскольку на этом примере можно увидеть много интересного для руководителей подобными работами. Контракт с «Нью-Йорк таймс» с заранее установленной суммой достался фирме IBM в 1969 г. в результате соревнования с другими фирмами. Наши люди из Гейтсбурга были встревожены тем, что плата, запрошенная фирмой IBM, была на 800 000 долларов меньше того 1 млн., который запросили другие фирмы. В то время в Центр федеральных систем прибыл мой предшественник. Это был Генри Уайт, прекрасный, очень способный руководитель. Он организовал проверку, которая показала, что фирма не уложится в предполагаемую цену. Рассчитывали, что после исправления можно будет объединить некоторые уже существующие программы поиска с уже существующими системами, а они оказались несовместимыми! Было похоже на то, что потери составят около 800 000 долларов.
Уайт обратился к X. Милсу. Милс только за год до этого поступил в фирму IBM после выдающейся карьеры в качестве математика и администратора. Он работал в «Дженерал электрик», в Американской радиокорпорации, в Математическом институте. Он был среди других технических работников в распоряжении Уайта. Именно ему Уайт и поручил работу по контракту с «Нью-Йорк таймс».
Милс сразу согласился, выбрал главным программистом Бейкера, а его помощником Мейера. Для ведения всех необходимых записей и канцелярских дел им в помощь был выделен библиотекарь.
Группа работала в Гейтсбурге, очень часто наведываясь в Нью-Йорк. Они выполнили свою работу — 83 тыс. операторов на языке высокого уровня — превосходным образом за 22 месяца, затратив около 300 000 долларов. Ошибок в системе практически не было, она была готова в срок, система была создана и заработала! Вот так-то! Мы сэкономили около 700 000 долларов!
Когда я (я вступил в свою должность в декабре 1969 г.) выдавал Милсу, Бейкеру и другим крупную денежную премию, я обнаружил, что во всей фирме IBM никто не имеет никаких статистических данных по производительности труда! Сколько строк программы считается нормальным написать за один человеко-месяц? Никто этого не знал. Ни у кого даже не было точного определения «строки программы». Все были согласны с тем, что они заслужили премию, но во сколько раз они повысили производительность труда — в 5 раз (как считал я) или всего в 3? В результате мы решили внедрить в Центре федеральных систем систему измерений программ, которой постоянно пользовались в течение более чем 8 лет. Собирались статистики и данные по всем аспектам проектов; база данных содержит ныне колоссальное число данных по 100 проектам. Это единственное в своем роде собрание из всех, с которыми мне приходилось сталкиваться! Мы обратимся к нему позднее в этой же главе.
Мы постановили, что при разработке всех новых проектов надо пользоваться методами структурного программирования. К моему изумлению, реакция опытных программистов была крайне отрицательной! «Что понимает в программировании этот Милс?» «Работа для „Таймс“ это счастливая случайность!» «Все сделал Бейкер». «Милс и Бейкер — это не средние люди». С этим-то я согласен! Но мы настояли на своем, и это было правильное решение. Мы истратили уйму денег и сил на обучение более чем 2600 человек. Две недели занятий с 2600 людьми составляют 100 человеко-лет! Нет никаких сомнений в том, что это было правильное решение
Причиной того, что работа для «Нью-Йорк таймс» была выполнена с такой высокой производительностью, было то, что Милс и Бейкер еще в 1970–71 г. использовали множество самых передовых методов разработки программного обеспечения. Среди них:
структурное программирование,
метод главного программиста,
метод реализации сверху-вниз,
использование библиотекаря,
сквозной контроль,
формальные грамматики.
Именно успешный исход работы для «Таймс» породил путаницу, связанную с тем, что многие авторы стали включать все эти методы в одну категорию под общим названием структурного программирования.
Преимущества структурного программирования
Структурное программирование внесло порядок в царство анархии. Руководителю работ и инспектору оно подарило надежность! Наглядность. Наглядность стала делать программное обеспечение более управляемым! Обозримым! Стал возможным сквозной контроль! Появились стандарты и возможности обмена программами. Были прояснены сложнейшие проблемы. Разработка программного обеспечения вступила в новую эру.
Приемлемость структурного программирования
Многие программистские организации публично объявляют свою верность структурному программированию — и не пользуются его методологией. Некоторые до сих пор путают со структурным программированием другие хорошие методы. Большинство не знает точно, что это такое.
Любая достаточно крупная команда разработчиков программного обеспечения, не пользующаяся четко определенными методами структурного программирования, не может считаться достаточно компетентной. Мы вернемся к этому вопросу и продолжим обсуждение причин сопротивления структурному программированию в гл.6.
Хорошее проектирование
Хорошее проектирование приводит к ясному проекту; в таком проекте имеется внутреннее единство. В своей книге «Мифический человеко-месяц» Брукс упоминает европейские соборы, строившиеся по нескольку веков. Наиболее эффектны из них те, в которых видна идея самого первого архитектора. К «плохим» относятся те соборы, в которые каждое столетие вносилось что-нибудь от очередного строителя. Многие языки программирования не прижились потому, что их создатели попытались объединить в них слишком много разноречивых свойств. В связи с этим Брукс упоминает язык PL/1.
С концепцией единства проектирования согласны и Милс, и Уитт, и Лингер, с ней согласны также Йенсен и Тонис. Мой опыт говорит, что автором проекта больших систем является обычно один человек. Этот человек определяет направление работ.
Как говорится, о вкусах спорить не приходится. Система диспетчеризации воздушных перевозок доводилась до рабочего состояния в течение 10 лет (совершенно завершена эта работа не будет никогда), за этот период времени заказчик провел немало разных ревизий. Одна ревизия, которую проводила группа независимых экспертов по программному обеспечению, констатировала, что проект управляющей (системной) программы выполнен «архаичными методами и неправильно». Несмотря на это, после завершения работ программа прекрасно работала. (Но скольким толкам дал пищу этот отзыв!)
Некоторые руководители разработок программного обеспечения толкают своих программистов на постоянное использование новейших, самых «привлекательных» методов. Они пытаются привычные вещи делать новыми способами или находить новые работы. Этих программистов снабжают инструментарием, специально проектировавшимся с расчетом на творческое использование. Мы должны называть такие работы не разработками программного обеспечения, а научными исследованиями.
С другой стороны, находятся такие руководители, которые уже закончили проект и теперь хотят, чтобы были написаны программы размером в 400 000 строк или около того. А после того как программы будут написаны, их надо будет отправить пользователям и заняться их сопровождением. Этим руководителям не нужна ни привлекательность, ни изобретательская деятельность. Им требуется надежность, ясность, воспроизводимость. Им нужны простые, незатейливые программы.
Проектирование это…
Проектирование — это все же искусство и что-то еще, что мы еще недостаточно понимаем. Стравинский называл искусство «творчеством, требующим скачка воображения». Может быть, и существуют проекты, которые не являются произведениями искусства, а возникают в результате простого следования образцам — проекты мостов, зданий, одежды и т. д. — но нужно ли нам называть их проектами?
Итеративность
Мы обнаруживаем то, что не работает, и исправляем это место. Когда мы первый раз выполнили мультипроцессорную обработку в системе диспетчеризации воздушных перевозок, то обнаружили, что процедура блокировки (дающая гарантию того, что ни второй, ни третий, ни четвертый центральные процессоры не смогут работать с той записью, которая обрабатывается в данный момент, и не нарушат достоверность данных) работала слишком уж хорошо. Нашим центральным процессорам приходилось лишь лениво дожидаться, когда будет снята блокировка. На втором проходе мы укоротили все процедуры, выполнявшиеся в режиме «блокировки».
Компромиссы
При проектировании возникает много компромиссных решений, одно за счет другого — например увеличение занимаемой памяти и за счет этого повышение скорости выполнения программы. Так же как авиаконструктор постоянно жертвует ради увеличения веса и скоростью, и экономичностью двигателя, так и проектировщик программного обеспечения должен принимать во внимание все 12 свойств программ во всех их взаимоотношениях и еще множество других характеристик, которые связаны и с программой, и с теми функциями, которые ею автоматизируются. Что произойдет с остальной частью системы? Что изменится в радиолокаторе? В окружении? Не поглотят ли наш банк другие, более крупные банки? Или его просто вытеснят из данной сферы деятельности?
Проектирование больших программ типа V — это многогранная деятельность
Мы уже видели, что в работе над созданием программы для приложений типа V гораздо меньше требуется усилий, чтобы написать, чем для того, чтобы понять поставленную задачу и свои возможности, сформулировать требования, понять их и объяснить проектировщикам и кодировщикам. Для понимания требований обычно нужны опытные, профессионально подготовленные специалисты, которые либо ранее занимались автоматизацией этой же области, либо имеют достаточную квалификацию, чтобы воспринимать информацию от тех, кто выполняет работу в настоящее время.
Если мы, например, заняты автоматизацией управления посадкой на Луну, то в нашу группу необходимо включить тех, кто разбирается в физических процессах прилунения, кто понимает гравитационные явления, происходящие на Луне, знает, как подействует удар при посадке на двигатель и т. д.
Эти знания требуются постоянно. Неверно представлять себе, что нам удастся в самом начале выяснить все, что нужно, и затем пользоваться только этим. Эти знания являются зеркалом, в котором отражается все, что мы делаем. Нам не удастся просто написать требования раз и навсегда. Мы будем постоянно возвращаться от требований к проектированию, от проектирования к программированию и назад к требованиям. Специальные эксперты должны контролировать проектирование, его правильность надо проверить до того, как начнется собственно программирование.
Процесс, изображенный на рис. 5.29, выглядит красивым и аккуратным, однако вряд ли возможно с помощью стрелок изобразить умственные усилия, необходимые для успешного его завершения. Нельзя также изобразить тот горячий спор, эмоциональный всплеск, который возникает, когда эксперты (точнее, выдвигаемые ими требования) отвергают некоторый подход (проект) как «рискованный», или «запутанный», или «неверный».
Деталировка — это примитивное проектирование
У нас был один проект, который принес нам убытков на 3 млн. долларов. Это была система редактирования данных в реальном времени, которую мы делали по заказу Национального управления по аэронавтике и космическим исследованиям, причем цена была заранее установлена. X. Милс постоянно говорил мне, что проект системы отсутствует; исполнители же говорили, что работают по проекту.
Я назначил на один из дней проверку, и исполнители работ докладывали в течение 10 ч.! У них было огромное количество графиков, расчетов, бумаг и данных.
Через 10 ч. ответ был ясен! У них был проект, метод, казавшийся вполне осуществимым. Все это было сосредоточено в двухметровой куче графиков и в головах двух ведущих сотрудников. Для нас, слушателей, проект оставался недоступным. Некоторые проблески его мелькали перед нами в течение этих 10 ч. Мы убедились, что проект существует, но увидеть его мы не могли, никто из нас не мог его отчетливо сформулировать.
Милс, Лингер и Уитт назвали это «деталировкой». Она может служить в качестве проекта, но на самом деле является лишь его бледной тенью. Деталировка часто «удовлетворительна» — однако выбирается всегда первое попавшееся решение, поиски более хороших решений не производятся.
Устойчивые программы, или программы, дружественные пользователю
Очень многие программы после работы групп определения требований и разработки передавались заказчику («пользователю»), а затем никогда не использовались. Они были слишком сложны в обращении либо неверно выполняли требуемые функции. Мы уже рассказывали об инциденте в Джексонвилле, когда центром диспетчеризации была отвергнута программная система, выполнявшая слишком много функций. Здесь мне хотелось бы ненадолго остановиться на той части программы, которая управляла взаимодействием с человеком.
В тот момент, когда мы подключили людей к диалогу с вычислительной машиной (типы III, IV и V), задачи, стоявшие перед нами, значительно возросли. Мы должны писать теперь программы, которые могут давать людям возможность взаимодействовать — вести диалог с системой.
Я однажды работал с небольшой группой программистов — всего 7 человек, — занимавшейся созданием диалоговой программы по анализу многозначных решений. Программы писались на языке APL; их размер составлял 10 тыс. операторов языка APL — из них 9 тыс. операторов было написано для управления интерфейсом с человеком. Девять из десяти. Нам необходимо было сделать так, чтобы надежды пользователей оправдались, иначе нашей системой не стали бы пользоваться.
Если необходимо было ввести букву О, а пользователь набирал цифру 0 либо наоборот — мы не ограничивались тем, что сообщали «неверные данные на входе». Мы давали подсказки, чтобы помочь поддержать процесс активным. «Вы хотели ввести символ нуль или О?» — выдавали мы на экран.
Проектирование этой части программы имеет решающее значение и очень сложно. Обычно после того, как пользователь получит возможность опробовать систему, эта часть подвергается значительным переделкам. Она имеет наибольшее значение для того, чтобы пользователь принял систему.
Документирование проекта
Если отсутствует документация, доступная для всех, имеющая несколько уровней детализации, проекту грозит беда. Под несколькими уровнями детализации я подразумеваю то, что проект может быть описан за 30 мин, за 2 ч, за 8 ч, или за 16 ч. Если имеется только 30-мин. обзор или 16-ч. версия и ничего между ними, остерегайтесь. Почему?
Потому что проект начинает существовать только в голове одного, в крайнем случае двух проектировщиков. Мы лишены возможности ухватить идею и следить за развитием проекта на уровне подробностей 16-ч. версии, если только мы не станем заниматься этим проектом все свое время и не познакомимся с ним достаточно близко.
Наша группа по разработке системы выпуска газет США имела два уровня проекта — 30-мин. и 16-ч. Я знал и руководителя работ, и проектировщика — соответственно Б. Уитта и Т. Бейкера. Они не знали неудач и в прошлом уже выполнили просто фантастические работы. И я доверял им! Но что случилось бы, если бы кто-нибудь из них заболел или уволился? На карту была поставлена судьба целого проекта — миллионы долларов.
Проекты таких больших систем должны быть документированы, чтобы их можно было контролировать на всех уровнях. На это нужны и деньги и таланты, но не делать этого — значит, играть с огнем.
Спецификация — это проектирование и одновременно выработка требований
Проект отвечает на вопрос КАК; требования отвечают на вопрос ЧТО.
Если на листке бумаги я напишу одно лишь слово «автомобиль» и попрошу вас достать мне такую штуку, содержанием надписи будет и «что», и «как». Для вас это будет «что»; это автомобиль, вам надо пойти и достать его. Для меня это будет «как», некоторая определенная конструкция, отличная от мотоцикла, грузовика, велосипеда и всех других механических транспортных средств. Я буду путешествовать «как»? На автомобиле.
С одной точки зрения некоторая вещь кажется проектом; с другой — она же кажется требованием, (см. рис. 5.30.)
Если уж слово «автомобиль» может быть и требованием, и проектом, то и подавно документ в 100 страниц может быть и проектом и требованием.
Я не могу описать все часы интенсивной борьбы на самых высоких уровнях, которые пришлось мне испытать, решая важную проблему, чем является некий документ — проектом или требованием. Он выполняет обе функции!
Новые методы проектирования
В нашем распоряжении имеется чересчур много новых методов проектирования. Многие из этих методов являются в сущности методами записи, а не ведения процесса. Они достаточно ценны, поскольку любая помощь при проектировании только приветствуется. У них множество имен: метод Вернера — Орра и Джексона, метод HIPO, SADT (технология структурного анализа и проектирования), IA (информационный анализ), структурное проектирование — все они лучше, чем отсутствие какого-либо формализма при проектировании. Все они большей частью связаны с документированием и демонстрацией проекта, чем с его созданием, причем в значительной степени это относится к последним двум стадиям — структуризации и представлению, чем к первой — созданию.
Данные и процесс
В большинстве методов делается упор на процесс и лишь затем переходят к данным. Немного найдется сторонников обратного перехода — от данных к процессу.
С чего начинать — с данных или с процесса, — зависит от прикладной области или от проектируемой системы: что в данном случае имеет превалирующее значение. В больших системах типа III это данные, поэтому проектирование нужно начинать с них. Большие системы типов IV и V, вероятнее всего, следует начинать с проектирования процесса.
Структурное проектирование
Этим словосочетанием определяют столько различных методов документированного проектирования, что всякие слова тут практически бесполезны. Что значит структурное? Этот термин не имеет совершенно никакой связи с термином программирование!
Тот факт, что такое множество новых методов возникает именно в области проектирования, хотя бы только в его документировании, очень обнадеживает. Почти во всех методах имеются довольно строгие правила, говорящие, как надо отмечать что и как, а также определяющие последовательность переходов. Почти во всех имеются правила управления всеми уровнями детализации, которые могут потребоваться, и правила, помогающие пользователю избежать ненужных ему подробностей.
Известно, что обмен сложными идеями и концепциями является основной трудностью при управлении сложными системами, поэтому мы должны быть благодарны за любую помощь, которая оказывается нам в этой области. Большинство новых методов очень полезно именно в данном отношении, поэтому мы рекомендуем всем их использовать.
Трудности нововведений
Как и всякая новая человеческая деятельность, новые методы проектирования оказываются тоже очень трудными для введения. Изменениям сопротивляются; правила раздражают; строгости кажутся бесполезными. Но это бывает только в начале. Как и многие другие полезные новые методы, эти методы в конце концов признаются «единственно возможными», а на все старые при этом начинают смотреть с изумлением и весьма критически.
Это происходит со всеми новыми методами — как мы уже видели, не было исключением и структурное программирование. Человеку нужна по крайней мере неделя, а обычно даже две недели формального изучения и затем несколько месяцев тренировок, чтобы окончательно признать новый метод. Тут нужны контроль и принуждение. Весь наш персонал, занятый в информационных системах — программисты, аналитики и другие, — нуждается в таком «вдалбливании».
Если группа разработки и внедрения информационной системы состоит из нескольких сотен человек, цикл обучения получится весьма дорогостоящим. Его, однако, необходимо провести. Результаты настолько значительны, что пренебрегать новыми методами нельзя.
Написание программ — программирование
Мы приступаем к изучению деятельности, которую многие ошибочно считают разработкой программного обеспечения, а именно к изучению процесса написания программ. Это всего лишь часть общего процесса разработки программ. В больших системах эта часть является самой простой из всех шести частей разработки. В небольших проектах эта часть требует наибольшей доли времени.
В какой-то момент проектирование доходит до такого низкого уровня, что для того, чтобы заставить вычислительную машину выполнить то, что задумал проектировщик, достаточно написать соответствующие команды.
Для очень маленьких программ, которые пишутся одним, двумя программистами, единственной загвоздкой тут является выбор инструмента, т. е. языка, на котором следует писать. А при программировании больших программ этот вопрос также является одним из ключевых.
Наше изучение процесса написания команд мы разделим на три части — выбор языка программирования, процесс доведения программы до выполнения и управление этими двумя процессами. Мы ограничимся только изучением деятельности по написанию маленьких программ. Написание небольшой программы (до 10 тыс. строк текста программы) не так уж сильно отличается от написания программы в 1 млн. строк; различия скорее находятся в других частях процесса их разработки. Поскольку различия в написании невелики, мы будем обсуждать проблемы, не уделяя внимания дополнительным обязанностям, имеющимся у программистов при программировании программ в 1 млн. строк. Конечно, различия все-таки есть, все дополнительные обязанности мы обсудим в следующем разделе, посвященном компоновке. Дополнительная нагрузка на программиста обычно заключается в некоторых ограничениях и устанавливает определения данных, констант и интерфейсов.
После того как программист выяснил алгоритм и структуру программы, он начинает писать команды.
Написание программы обычно выглядит как акт записи команд на листке бумаги, которые, возможно, будут переданы на машину. После того как команды будут записаны, их надо перенести на какой-нибудь носитель информации, с которого их сможет считать машина и с которого они могут быть загружены в вычислительную машину, чтобы проверить, выполняет ли программа то, что от нее требовали. Сотни тысяч программистов водят карандашом по бумаге и пишут команды. Они занимаются «кодированием». В настоящее время все большее число программистов вводят в машину команды, пользуясь терминалами, но большинство делают это, уже имея готовый текст.
Важнейшие моменты, связанные с написанием текста программ, таковы:
1. Ясность того, что нужно сделать — это мы уже обсуждали.
2. Язык, который будет использоваться, и стандарты, которым необходимо следовать.
3. Процесс управления преобразованием программ с языка программирования на машинный язык.
4. Инструментарий для реализации пп. 2 и 3.
Языки
Лингвисты, занимающиеся семантикой языков, до сих пор продолжают открывать в языках новые свойства. Все еще продолжаются дебаты по вопросу, что важнее — разговорный язык или язык письменный. В книге «Введение в теоретическую лингвистику» Джон Лайонс отмечает, что язык — это не просто свод правил, лингв, это и не собрание всех возможных устных и письменных форм слов, но и то и другое вместе. Даже неправильно произнесенное, синтаксически неправильное выражение может быть понято. Это происходит потому, что слушатель заполняет пропуски и исправляет ошибки. К сожалению, вычислительная машина и ее транслирующая программа (ассемблер или транслятор) делают только то, что им приказывают. Даже самые хорошие трансляторы имеют весьма ограниченный интеллект. Для языков вычислительных машин синтаксические правила приобретают решающее значение.
Наверное, не очень очевиден тот факт, что процесс получения команд основывается на трех важнейших положениях:
1. Вы должны понимать процесс, для выполнения которого пишутся команды.
2. Вы должны уметь сформулировать (выразить словами) последовательность команд.
3. Вы должны выбирать такие слова, которые будут поняты вашими собеседниками.
Мы часто предполагаем, что п.1 выполняется; что это не так, мы видели в разделе, посвященном определению требований. Мы также предполагаем, что человек, который может «выполнить процесс», может и облечь его в словесную форму, — это тоже не всегда так.
Теперь обратимся к третьему пункту — написание команд для конкретного исполнителя. В нашем случае исполнителем оказывается вычислительная машина, но, прежде чем переходить к командам для вычислительных машин, я хотел бы обсудить некоторую аналогию.
Если бы я был великим нейрохирургом, я мог бы сформулировать инструкции для разрабатываемой новейшей хирургической техники, у меня не было бы трудностей по выдаче таких инструкций другим нейрохирургам. Введите в мою аудиторию обычных хирургов, и моя задача сразу усложнится. Добавьте туда студентов-первокурсников медицинского института, и я не уверен, что вообще смогу выполнить свою задачу. И так далее.
Так же и с вычислительной машиной. Словарный запас ее крайне примитивен. Заставить ее что-то сделать, сказать это на ее родном языке, называемом машинным языком, исключительно трудно. И все же мы должны осуществлять сложные логические построения именно с таким словарным запасом, поскольку только на такие слова машина и может реагировать.
Самые первые пользователи вычислительных машин сразу поняли, что вести дела таким образом невозможно, поэтому и «наняли переводчиков».
Возвращаясь к аналогии с нейрохирургией, заметим, что этот процесс выглядит так — выдающийся хирург будет теперь передавать инструкции посреднику, который переведет их на язык, доступный студентам-первокурсникам медицинских институтов.
Так же обстоит дело и с вычислительными машинами. Мы больше не заставляем программистов пользоваться машинным языком, а переводим их на работу с языком высокого уровня. Мы нанимаем в качестве переводчика ту же самую вычислительную машину, которая будет выполнять процесс после его перевода (или трансляции), и помещаем в нее программу, которая будет осуществлять перевод: либо ассемблер, либо транслятор, либо и то и другое вместе (рис. 5.31).
Для того чтобы машина могла выполнить перевод, в нее должны быть введены программы ассемблера или транслятора.
Транслятор воспринимает операторы языков высокого уровня, т. е. Фортрана, Кобола, Паскаля, Бейсика, и «заставляет» вычислительную машину преобразовать эти операторы в команды на машинном языке, которые затем можно будет в свою очередь поместить в машину и выполнить с целью решить реальную задачу — например напечатать платежную ведомость.
Такой процесс выполняется по крайней мере в два этапа. В нем присутствуют этап трансляции и этап использования. Пока никто даже не приступил к созданию машины, которая могла бы сразу понимать языки высокого уровня.
Можно отметить, что в аналогии с выдающимся нейрохирургом он теперь не должен мучиться, объясняя свои операции на языке новичков. Так же и программисты. Языки высокого уровня позволяют им писать ближе к собственному уровню.
Мощность языка и связанные с ней трудности
Чем мощнее язык, тем труднее его изучить, так же как трудно выучить медицинскую терминологию и трудно заучивать слова с многозначной семантикой.
Если пытаться построить график зависимости легкости производства хорошего программного обеспечения от легкости обучения языку, получится нечто вроде того, что представлено на рис. 5.32.
Под хорошим программным обеспечением я подразумеваю хорошо скомпонованные, легко просматриваемые, хорошо документированные программы. Значит ли это, что нам никогда не следует пользоваться Бейсиком? Конечно, нет. Мы иногда пишем и такие программы, для которых не нужна ни легкость модификации, ни легкость расчленения.
Итак, выбор языка зависит от того, что мы собираемся писать. На графике не отмечены языки APL и LISP. Эти языки применяются не для построения сложного программного обеспечения, а для решения задач. Программы на машинных языках, получающиеся после трансляции с данных языков, не имеют большого значения; в большинстве случаев их даже не видят и не сохраняют. Я просто не могу себе представить, чтобы на APL кто-нибудь решился строить большую систему программного обеспечения. На рис. 5.33 изображены процедурно-ориентированные языки.
ПОЯ — другой тип языков. ПОЯ означает проблемно-ориентированный язык, или, иначе, процедурно-ориентированный язык. Эти языки также находятся на более высоком уровне абстракции, чем машинные языки. Их проектируют специально для решения какой-нибудь задачи или выполнения конкретной процедуры. Эти языки, следовательно, хорошо подходят именно для такого использования и не очень хорошо для других целей.
Среди этих языков заслуживают упоминания:
APL — решение задач
LISP — обработка списков
GPSS — универсальное моделирование
SIMSCRIPT — обработка текстов
ATLAS — язык министерства обороны США, применяемый в тестовом оборудовании
Целью этих языков является сокращение времени разработки программ. Делается это путем облегчения средств, с помощью которых создатель программы доводит программу до выполнения на машине. В этом смысле эти языки нельзя называть универсальными. Они реже используются при разработке регулярно используемых программ, чем при разработке программ, выполняемых либо единожды, либо очень редко.
Именно поэтому мы не будем уделять этим языкам чересчур много внимания.
Каждый язык представляет собой объединение словаря и синтаксиса. Словарь — это список слов, которые понятны транслятору. Синтаксис — это список правил расположения элементов словаря — порядок следования, знаки препинания, допустимые комбинации.
Когда вычислительная машина совместно с транслирующей программой воспринимает оператор языка высокого уровня, происходит сразу много разных действий. Начинается трансляция, а вместе с ней расширение. Когда я говорю своему помощнику: «Пойдите в библиотеку и принесите мне копию X», остается много не уточненных подробностей.
Помощник сам восполняет недостающие детали: какая библиотека, когда, как туда добраться, где находится X, как получить его и т. д.
Человек способен сам провести подобное расширение команды, поскольку он достаточно умен и хорошо осведомлен. Даже если он никогда не бывал в этом городе, он легко сможет выяснить, куда надо пойти, но мне говорить об этом все равно не было бы необходимости. Это же относится и к языкам машины и трансляторам, которые с ними работают. Многие трансляторы допускают использование неописанных переменных.
Многие языки программирования пытаются добиться эффективности в одной или в нескольких областях применения. Они могут облегчить человеку выражение глобальной идеи или сделать так, чтобы окончательный вариант программы исполнялся на машине как можно быстрее. Приспособлен ли язык к данной проблеме? К общению с человеком? С машиной? Или какой-нибудь комбинации этих свойств? Что нужно знать человеку для постановки задачи, ее решения, выполнения искомого процесса? Как думают люди? Как они общаются? Работают? Пишут? Ошибаются? Что может заставить машину работать более продуктивно? Всякую машину? А эту машину?
Вот потому-то десятки миллионов тратятся каждый год на проектирование и разработку новых языков программирования. Вот потому-то люди занимаются всю свою жизнь исследованиями в области языков для вычислительных машин.
Рост числа языков
К счастью для программистов, разработка языков программирования началась практически одновременно с появлением вычислительных машин. К сожалению, языков для них образовалось великое множество. В министерстве обороны США было подсчитано, что в его организациях используется около тысячи разных языков! Почему? Потому что программисты создают себе собственные языки программирования! И еще потому, что нет хорошего способа определить, какая версия лучше, а какая хуже, даже при использовании. И еще потому, что до сих пор языки оставляют желать лучшего.
В то же время наметилась некоторая стабилизация. Министерство обороны выбрало семь языков (инструкция 5000.31), на которых будут писаться все новые «встроенные» системы реального времени (Имеются в виду языки: Фортран, Кобол, Джовиал, CMS-2, SPS, TACPOL и TOS).
После длительного и глубокого изучения министерство обороны представило новый язык Ада, основанный на некотором расширении Паскаля, разработанного Н. Виртом. Если Ада докажет свою полезность, она будет включена в список инструкции 5000.31. Это очень мощный, очень сложный язык.
Однако программисты предпочитают старые языки. Сообщество программистов непоколебимо отказывается переходить на работу с новыми языками программирования. Подавляющее большинство программистов продолжают писать на Коболе и Фортране, несмотря на то что оба этих языка в настоящее время считаются устаревшими.
Почему же все старые языки продолжают существовать и остаются столь популярными? Они были изучены первыми! Тони Хоар в своем письменном докладе военно-морскому флоту констатировал следующее:
Привязанность программистов к существующим языкам сильнейшим образом контрастирует с энтузиазмом различных комитетов, утверждающих, что языки не отвечают поставленным требованиям. Причина? …затрачено огромное количество времени на овладение языком, новый же язык представляет в этом отношении серьезную угрозу, или, что еще хуже, новый язык может оказаться даже более простым для изучения и более эффективен в использовании, что может совершенно подорвать с огромным трудом завоеванное положение эксперта по старому языку…
Таким образом, может получиться и так, что более легкий язык будет побежден сложным. Программисты из Советского Союза рассказывают аналогичные истории. По-видимому, одним из первых полученных ими языков был Кобол — англоязычный COBOL. Видимо, первые вычислительные машины попали туда из Соединенных Штатов и программисты должны были учить английский язык, для того чтобы воспользоваться оператором PRINT. Англоязычный COBOL выжил! Потому что старики были настроены против русскоязычного Кобола. «Пусть новички помучаются». Англоязычной версией Кобола пользуются до сих пор и в Германии.
После того как уже написано большое количество программ, перейти на новый язык трудно. Как говорят юристы, владение имуществом почти равносильно праву на него. Желание не расставаться с накопленным багажом неистребимо.
Язык и мышление
Язык, которым мы пользуемся, очень сильно воздействует на методику нашей работы. Многие утверждают, что язык формирует мышление. В книге, посвященной стилю письма и коротко названной «Стиль» Ф. Л. Лукас пишет:
Вот прекрасная история о человеке, зашедшем в кафе для иностранцев, в котором уже сидели группы англичан, французов и немцев. Человек этот обратил внимание на то, что англичане в полном молчании сидели вокруг своего столика, французы говорили все разом, а немцы слушали друг друга по очереди с таким напряженным вниманием, что на мгновение поразили нашего героя. Потом он понял — они ждали конца фразы, чтобы услышать глагол (или его отрицание).
Это же можно отнести и на счет языков программирования. Выбор языка оказывает на весь дальнейший процесс глубокое воздействие. Почему Вирт предложил Паскаль? Потому что ему надоело учить студентов языкам, которые «слишком часто нельзя было логически объяснить».
Нотация, или система обозначений, помогает процессу мышления. Уайтхед и Рассел («Principia Mathematica», 1910) объяснили, что хорошая система обозначений способна освобождать мозг от излишних деталей и переключать его на обдумывание других вещей. Мощные языки типа APL дают возможность программисту заставить машину выполнить огромную работу с помощью весьма немногих команд.
Конструкция языка ограничивает возможности мышления. В лекции, прочитанной при вручении ему премии Тьюринга, Э. Дейкстра рассказал историю одного проведенного им эксперимента. Он разослал нескольким своим корреспондентам условие одной задачи и попросил их прислать ему алгоритм ее решения. Все они прошли мимо самого очевидного и элегантного решения, сказал Дейкстра, поскольку их понятие о повторных вычислениях было слишком тесно связано с представлением об управляющей переменной цикла и ее пошаговом изменении.
Ограничения, накладываемые языками
Уже с давних пор мы тщетно бьемся над созданием современного языка. Приведу обширную цитату из статьи Якоба Броновски «Логика ума» в сборнике «Чувство будущего».
Проблема принятия решения … такой поразительный вопрос сформулировал Давид Гильберт: очевидно ли, что … все математические утверждения, имеющие смысл, будут рано или поздно отнесены либо к истинным, либо к ложным…
В 1931 г. … Курт Гёдель доказал две замечательные и встреченные крайне недоброжелательно теоремы. В первой говорилось, что … любая не слишком простая логическая система может заключать в себе истинные утверждения, которые тем не менее не могут быть выведены из ее аксиом. Вторая теорема утверждала, что … про аксиомы такой системы … нельзя заранее сказать, что они свободны от внутренних противоречий. Коротко это можно сформулировать так — более или менее богатая логическая система никогда не может быть полной, и в то же время нельзя гарантировать ее непротиворечивость…
А. М. Тьюринг в Англии, а Алонсо Чёрч в Америке показали, что нельзя построить механическую процедуру, позволяющую проверить все утверждения логической системы и за конечное число шагов установить их истинность или ложность…
Тарский показал, что нельзя построить точный язык с универсальными свойствами; всякий формальный язык, который по крайней мере так же богат, как арифметика, содержит правильно построенные предложения, про которые нельзя сказать, истинны они или ложны…
Нельзя построить аксиоматическую систему, которая бы производила описание окружающего нас мира в полном соответствии с действительностью, причем каждой точке мира соответствует точка описания; в некоторых точках будут оставаться пробелы, которые невозможно заполнить с помощью дедукции; в других же точках может оказаться возможным прийти к противоположным выводам…
И наконец, теория Тарского продемонстрировала, и я думаю, что окончательно, что универсальное описание природы на едином замкнутом, непротиворечивом языке построить нельзя…
Это основной момент: язык, которым мы пользуемся для описания природы, предписывает (классификацией своих определений и аксиом) как форму, так и ограничения на открываемые нами законы…
Особенностью человеческих языков является то, что они построены на основе старых метафор и аналогий, которые служат благодатной почвой для изучения двусмысленностей и обнаружения скрытого сходства..
Многие логические проблемы вырастают из этого общего корня, а именно система значений для любого разумно богатого языка непременно включает в себя объекты самого языка. Это создает бесконечный спуск, бесконечный зал с отражающимися друг в друге зеркалами. Этот спуск попадает в фокус всех парадоксов логики, которые в каком-то смысле сродни классическому противоречию, известному еще грекам: они называли его критским парадоксом (или «парадоксом лжеца»)…
Бертран Рассел (совместно с Альфредом Нортом Уайтхедом в их общей книге «Principia Mathematica») попытался развязать узел парадоксов этого типа и положить конец нескончаемому потоку утверждений об утверждениях, построив теорию типов. Она предназначалась для того, чтобы удержать нас от использования того же самого языка для обсуждения вещей, которые этот язык обозначает, и для обсуждения фактов самого языка. Человеческий язык богат потому, что мы думаем о самих себе. Мы не можем исключить из человеческих языков ссылки на самого себя и при этом не превратить его из подлинно информационного языка в язык машинных команд…
Всякое размышление о мышлении обязательно включает ссылки на самого себя: первое же положение в философии Декарта, «Cogito, ergo sum», содержит такую ссылку. …Никакая логическая машина не может разрешить все трудности и парадоксы, создаваемые ссылками на себя…
Машина не является природным объектом, это человеческий артефакт, который подражает нам и пользуется нашим представлением о природе.
«СТРАННЫЕ ПЕТЛИ», о которых пишет Дуглас Хофстедтер в книге «Гёдель, Эшер, Бах: Вечная золотая лента», являются расширением этих идей.
А в своем письме-докладе ВМФ США Хоар писал:
…а те, кто должен внедрять только что спроектированные языки высокого уровня, сталкиваются с множеством невероятно сложных частных случаев, неоднозначностями и противоречиями. Нужно создавать комитеты по стандартизации … на это уходит не меньше 6 лет. Такая работа «разбивает души», ведь очевидно, что «правильного» разрешения всех неточностей начального проекта не существует…
От возникновения языка до его «успешной стандартизации» проходит 10 лет…
Это относится к Фортрану, Алголу и PL/1. Паскаль не стандартизован до сих пор.
Почему? Из-за необычных, сложных и неожиданных взаимовлияний одних разделов языка на другие.
Паскаль имеет слишком высокий уровень, чтобы можно было с удобствами проводить (например) двоичный ввод/вывод; этот язык имеет недостаточно высокий уровень для того, чтобы обеспечить полную гарантию безопасности при работе с записями. Включение в язык элементов как высокого, так и низкого уровня является очень сложной проблемой.
Поскольку вред наносят именно эффекты взаимодействия, нужно изучать весь язык, чтобы понять, что следует исключить из него.
Основная заслуга языка высокого уровня в том, что он помогает разуму при проектировании и документировании программ для вычислительных машин; помощь, оказываемая им при непосредственном кодировании, может иметь второстепенное значение [28] .
В недавнем докладе (1978) военно-морскому флоту исследователи насчитали более 2570 различных возможностей или свойств, которые можно выделить в языках программирования. 2570!!
Какими же богатыми оказываются наш мозг и наши языки!!
Кроме сформулированных Броновски естественных сложностей богатых языков, существуют еще и проблемы, связанные с оптимизацией, возникающие при построении и языков, и трансляторов с них. При оптимизации одной из 12 характеристик невольно ущемляются интересы остальных.
Написав транслятор, который проводит трансляцию очень быстро, мы плохо повлияем на скорость выполнения рабочей программы. И наоборот.
Процесс написания программы
Процесс написания программы представляет собой итеративный процесс, состоящий из собственно написания, исполнения и исправления. Исправления потому, что редко какая программа с самого начала не имеет ошибок. В зависимости от задачи и от самого программиста эти три шага могут повторяться несколько, иногда очень много, раз. Программист пишет команды транслятору, который в свою очередь переписывает их на язык — более детальный — вычислительной машины. Но транслятор это не человек. Он не понимает полутонов, жестов, тонких намеков, интонаций, выражений лица и еще множества символов, присутствующих в общении человека-переводчика и говорящего или пишущего. Вычислительная машина плюс транслятор абсолютно педантичны. Необходимо, чтобы были правильными все запятые, пробелы, тире, а также их места — все, как определяется языком.
Когда программист представляет свою программу на трансляцию, первое, что делает транслирующая программа, — это проверяет все синтаксические ошибки, а если таковые находятся, исходная программа отвергается; это сопровождается напечатанными сообщениями о том, что в такой-то и такой-то точке программы найдены ошибки такого-то и такого-то типов. Пропущена скобка. Целое число умножается на число с плавающей точкой (в некоторых языках это запрещено).
Дж. Вейнберг указывает, что «типичный программист» в своей деятельности по доведению программы до выполнения на машине, проходит два этапа. Сначала он стремится добиться первой безошибочной компиляции. Компиляция — эта трансляция и расширение (добавление нужных программ). «Безошибочность» заключается в отсутствии синтаксических и очевидных логических ошибок, поскольку первый шаг состоит в получении «семантически правильной» программы. Для получения такой программы программист всякий раз при перезапуске программы на машине изменяет в ней несколько операторов.
Через некоторое время предпринимается и второй шаг. Команды на машинном языке загружаются в вычислительную машину и исполняются обычно с использованием тех значений данных, которые программист задал наряду с командами. Результаты выводятся на печать и возвращаются программисту. Программист изучает их с целью определить, правильна ли логика работы программы, причем обычно оказывается, что она неправильна. Он исправляет логику процесса и снова перезапускает программу. Когда выясняется, что программа работает правильно, она либо наконец вычисляет то, что нужно, если это отдельная программа, либо передается кому-то другому, кто будет включать ее в более крупную программу. Процесс этот выглядит так, как это изображено на рис. 5.34. Проверка синтаксиса трансляторами — это весьма полезный процесс, помогающий программистам находить не только ошибки в пунктуации, но также и некоторые логические ошибки.
Если, например, программист написал программу, в которой управление передается подпрограмме по имени СЛЕД, но ни одну из особых подпрограмм этим именем не назвал, транслятор отметит этот логический промах.
Кросс-транслятор
Язык высокого уровня типа Фортрана может использоваться в качестве входного языка на самых разных вычислительных машинах, имеющих свои собственные транслирующие (компилирующие) программы. Язык стандартизован, каждая машина имеет свою транслирующую программу и создает рабочую программу именно для себя.
Логическим шагом в использовании этих транслирующих программ является использование машины и некоторого транслятора для перевода с исходного языка на язык другой машины.
Это называется кросс-трансляцией. Ее обычно применяют в тех случаях, когда машина, на которой будет выполняться рабочая программа, очень мала (по памяти, устройствам ввода/вывода или возможностям центрального процессора). Пускать трансляторы на маленьких, медленных машинах не очень эффективно.
Использование кросс-трансляции машины выглядит так. Программа пишется на входном языке; она переводится на большой машине транслятором, который выдает программу на языке второй машины. Затем на этой второй машине выполняется полученная рабочая программа. Вторая, маленькая машина обычно имеет систему команд, совершенно отличную от той, которая имеется на большой машине.
Этот процесс является логическим следствием преимуществ машинной независимости языков высокого уровня. Машинная независимость позволяет нам передавать программу, написанную на стандартном языке, на машину любого типа и модели, если только на ней имеется транслятор, который может переводить программу с языка высокого уровня на машинный язык. Схема процесса приведена на рис. 5.35.
Изменения и исправления в программу обычно вносятся на входном языке, с тем чтобы после этого вновь ретранслировать ее кросс-транслятором. На маленькой машине может транслятора вообще не быть.
Этот процесс значительно ускоряет программирование. Очевидно, что его недостатком является необходимость иметь большую машину.
Не так очевидно то, что указанный транслятор должен быть доступен группе, фактически продолжающей разработку. Случается, что разработчики программ при создании программы пользуются частными трансляторами, а затем возвращают их владельцам. В результате группа сопровождения лишается возможности транслировать исправляемые программы. Отвратительная ситуация. На создание «новых» трансляторов были затрачены миллионы!
Множество форм одной программы
Частую путаницу вносит тот факт, что программа одновременно может существовать во многих формах сразу. Причем именно этого мы и хотим. Нам нужна «версия» программы на языке рабочей машины (для исполнения) и версия на входном языке (для модификации). Подробнее мы обсудим это позднее, а сейчас давайте посмотрим, какие формы может принимать программа. Сначала посмотрим на простое деловое письмо.
«Где мое письмо?»
«Ваша рукописная копия лежит на вашем письменном столе; отпечатанная копия находится на столе у начальника; имеются версия, хранимая в системе обработки текстов, и версия, передаваемая по телефонным проводам во Флориду, кроме того, одна копия передается через спутник связи в ФРГ». (См. рис. 5.36.) Некоторые из этих форм писем могут быть прочитаны людьми: некоторые могут читаться только вычислительными машинами.
Такое же бывает и с программами. На рис. 5.37 изображено всего пять различных форм, в которых может существовать программа печати платежной ведомости.
Вычислительные машины для трансляции
Часто забывают о том, что для перевода нам нужен не только транслятор, но и вычислительная машина. Это кажется очевидным, но при моей работе над тремя проектами больших программных систем, в которых именно для трансляции не хватало вычислительной мощности, это привело к потрясающему росту затрат на разработку систем.
При разработке обеспечения для системы «Скайлэб» те вычислительные машины, которые использовались для проведения трансляции и построения системы, были переданы группе сопровождения для окончательного завершения работ по программе «Аполлон». Программисты писали программы, но трансляций было проведено не так уж много, а тестирование было просто невозможно. В конечном итоге был затрачен целый миллион долларов на аренду мощной машины IBM 360 модели 65, которая была необходима единственно только для сопровождения системы.
Во время работы по заказу военно-морского флота, проводившейся на Западном побережье, мои 40 программистов сделали очень мало, основной причиной чего была недостаточно большая и недостаточно комплектная инструментальная машина IBM 370. Программы писались для выполнения на машине фирмы Sperry UNIVAC UYK20, а трансляция проводилась на IBM 370/138. Дело оказалось не только в том, что эта машина слишком мала (ее мощности явно не хватало), но также и в том, что на ней имелся всего один канал, а, значит, возможности ввода/вывода были сильно ограничены. Нам пришлось переходить на машину модели 148, на которой было больше каналов. Наша группа плохо произвела выбор инструментальной ЭВМ.
Однажды в Европе меня познакомили с разработкой системы, состоявшей из программ в 1 млн. операторов на Коболе. Программы, входившие в систему, были слишком велики, чтобы из них можно было составить хорошую модульную систему. «Почему так?» — спросил я. По той причине, что в вычислительную машину, использовавшуюся для компиляции, было слишком тяжело вводить отдельные задачи. Это привело к тому, что проект обеспечения времени использования был изменен таким образом, чтобы уменьшилось время, необходимое на трансляцию. Трансляция одного большого модуля занимала меньше времени, чем трансляция нескольких модулей меньших размеров, выполнявших все те же функции.
Программирование в диалоговом режиме
Появление возможностей разделения времени обещало целую революцию в программировании. Казалось, время ожидания решения больше не будет самым узким местом, оно уменьшится до нуля.
Но это далеко не так. Логично предположить, что предоставление терминалов всем программистам увеличит их производительность, но доказательств этому пока нет. К тому же терминалы стоят денег. На основе базы данных по производительности, созданной в отделении федеральных систем IBM, «кажется», можно прийти именно к такому выводу, но люди, изучавшие этот вопрос, не хотят делать такого заключения.
В больших проектах, где заняты сотни программистов, за огромное число терминалов приходится платить очень дорого; эта тенденция катастрофически влияет на всю работу в целом, вся работа может превратиться в халтуру. Когда человек садится за терминал, у него возникает ощущение, что он должен его использовать во что бы то ни стало. Это совершенно противоречит стремлению к разумной, спокойной деятельности. Мы вернемся к этому вопросу, изучая проблемы производительности.
С чем же мы ведем диалог?
Давайте предположим, что я вошел в машинный зал и подошел к вычислительной машине. Никто мне ничего о машине не рассказывал, но я с радостью увидел, что это DEC 11/70, с которой я знаком, поскольку уже работал с ней. Я вижу, что машина включена и готова к использованию.
Возникает вопрос: пуста ли машина, ждет ли она самой первой команды? А есть ли в ней транслятор? Или операционная система?
В зависимости от ответов на эти вопросы я буду выдавать машине совершенно разные команды.
Если в машине ничего нет, я начну вводить команды с помощью тумблеров на внешней панели.
Если в машине есть ассемблер, а я хочу начать программировать, мне надо вводить команды на языке ассемблера.
Если в машине находится транслятор, я должен вводить операторы языка высокого уровня.
Если есть операционная система, мне необходимо знать, как этой операционной системе давать приказы, поскольку всей работой с машиной управляет именно она и мне нужно иметь дело именно с ней.
Никаких внешних признаков того, что там есть, не существует; поэтому если никто не подскажет мне, то мне придется применить метод проб и ошибок. Я попробую один из вариантов, который должен работать, если в машине находится то-то и то-то, потом посмотрю, работает ли этот вариант, и т. д.
В памяти вычислительной машины могут находиться или отсутствовать такие системы программ:
1) операционная система;
2) система управления базой данных;
3) система связи;
4) система управления дисплеями;
5) ассемблер (транслятор);
6) транслятор с ЯВУ;
7) прикладные программы.
Когда человек садится и пишет программу либо вводит ее через терминал, присоединенный к вычислительной машине, он может связываться либо непосредственно с аппаратурой (линия С на рис. 5.38), либо с целой цепочкой программ, написанных специально для программистов или пользователей. Человек на линии А ведет диалог с шестью программами и аппаратурой. Человек на линии В ведет диалог с одной программой и аппаратурой.
Очевидно, что по мере добавления различных программ и установления связи с ними мы получаем огромное число разных возможностей для ведения диалога. На рис. 5.39 изображены только некоторые способы связи программиста или пользователя с аппаратурой!
Если я нахожусь в точке A, я пишу на языке машины.
В точке В я использую язык ассемблера.
В точке С я использую язык транслятора.
В точке D я использую приказы операционной системы.
В точке Е я использую язык базы данных.
В точке F я использую язык системы связи.
Человек, программирующий на уровне E, не обязан ничего знать об уровнях, расположенных ближе к машине. Он может сколь угодно долго работать, не обращаясь к более глубоким уровням. Он имеет дело с абстрактной машиной, которая состоит из аппаратуры и определенных слоев программного обеспечения.
Все эти люди заняты программированием. Все они пишут команды, которые должны выполняться машиной. Команды проходят через транслятор или даже через целую серию трансляторов, а в результате получается группа машинных команд, которые потом исполняются аппаратурой.
Буквы со штрихами (E', F' и т. д.) обозначают не программистов, а пользователей. Обратите внимание на то, что их первое вхождение в вычислительную систему (аппаратура плюс программное обеспечение) осуществляется через прикладные программы. Пользователь вводит данные для прикладных программ, а результатом при этом будет не программа на машинном языке, а ответ ему либо несколько ответов.
Такое разнообразие способов ведения диалога с машиной вместе с ее программным обеспечением может вносить большую путаницу, поскольку все эти способы сильно отличаются друг от друга. Число способов, которыми можно взаимодействовать с вычислительными системами, поистине неограниченно. На нашем рис. 5.39 мы ограничились только одним прикладным уровнем, в то время как их могло бы быть и больше, в этом случае сообщения переходили бы с одного прикладного уровня на другой. Но и с одним показанным на рисунке уровнем число комбинаций доходит до 720 способов ведения диалога с аппаратурой и программным обеспечением с помощью терминалов. Это разнообразие вариантов является и благом и проклятием для вычислительных машин.
Точка / является хотя и необычной, но не такой уж редкой ситуацией. Используются сразу все слои программного обеспечения. Такой случай возникает, когда мне приходится из одного города вести диалог через диалоговый транслятор с вычислительной машиной, находящейся в другом городе. Вариант К (без использования уровня ЯВУ) встречается очень часто.
Управление написанием программ
Из-за чрезвычайной гибкости запоминаемых программ программирование стало очень трудным делом; одна и та же задача может быть запрограммирована сотней разных способов, ответ будет один и тот же. Все варианты будут работать, все дают одинаковые ответы! Может ли один быть лучше другого? Может, причем по-разному. Один быстрее выполняется. Другой требует меньше памяти. Мой вариант, возможно, был запрограммирован быстрее, чем ваш. То, что ваше решение «работает быстрее», компенсируется тем, что вы дольше писали вашу программу.
Число различных последовательностей команд почти беспредельно, ведь в своей работе программист связывает воедино множество шагов, которые он предпринимает, чтобы решить задачу. Кто может сказать что-нибудь о правильности или хотя бы о работоспособности программы до тех пор, пока она не будет опробована? До тех пор, пока программа не будет выполнена на вычислительной машине?
Очень трудно управлять чьей-нибудь работой и быть уверенным, что у него все в порядке, если число вариантов, которые тот может предложить, практически неограниченно.
Управление процессом написания крайне затруднено. Делают программное обеспечение трудным такие его свойства, как абстрактность и неосязаемость, необозримость и изменчивость.
Для помощи при управлении этим процессом предлагается использовать множество новых прекрасных методов.
Структурное программирование не только помогает процессу проектирования, но также делает окончательный вид программ более наглядным. Для отслеживания каждой команды (строки программы) в разрабатываемой программе создаются специальные программы, называемые автоматическими библиотекарями. Эти программы тоже сделали практическое программирование более наглядным. Они сделали программирование из частного дела общим достоянием.
Различия уровня квалификации программистов
Изучение программирования «в малом», т. е. небольших программ, показывает просто фантастическое различие в производительности и конечных результатах работы среди программистов. Различие в производительности, выраженной в строках программы в единицу времени, достигает 25 раз. Это означает, что некоторые люди пишут в день в 25 раз больше строк программ, чем другие. Размеры получающихся программ также отличаются, коэффициент доходит до 20; программы одних программистов могут быть в 20 раз больше программ других. Эффективность программ изменяется в диапазоне от 1 до 10; одни программы работают в 10 раз быстрее, чем другие, выполняющие ту же работу.
Однако совершенно не обязательно будет так, что программист, пишущий в 25 раз быстрее другого, будет хорошим программистом. Строки программы — это обманчивое, несовершенное средство измерения качества. Но это все, что у нас есть. Более или менее подробно мы будем изучать эту меру в гл.6. Здесь мы хотели только указать, что среди программистов имеются значительные различия.
Из всего сказанного и из приведенных цифровых данных надо сделать вывод, что необходимо проводить тщательный отбор сотрудников, набирать и выбирать хороших программистов, поручать людям работу, соответствующую их наклонностям.
Если ваше задание достаточно велико, будет крайне глупо оценивать результаты сотен программистов, основываясь на производительности лучшего из них.
Главный программист
Предположим, мы хотим задать вопрос: в какой момент следует разделить программу на две части и поручить программирование двум программистам? На этот вопрос нет полного всеобъемлющего ответа. Только в идеальном случае можно избежать разделения программы на отдельные части. С той минуты, как вы поделили работу и поручили ее выполнение двум работникам, они должны начать контактировать друг с другом, и тут же возникнут излишняя трата времени, путаница, появятся ошибки.
Очень многое зависит от работы, которую надо выполнить, от программиста, которому она поручена, времени, за которое нужно ее сделать, а также от тех средств, которые можно использовать при написании программ. В проекте системы для газеты «Нью-Йорк таймс» один программист, Т. Бейкер, за 22 мес. создал работающую программную систему, состоящую из 83 тыс. операторов на языке высокого уровня. Этим успехом мы обязаны тому, что в работе был применен метод X. Милса под названием «Группа главного программиста». Бейкер работал так, как работает хирург вовремя операции. Ему помогал библиотекарь, выполняющий все канцелярские дела, включая черную работу по отправке программ на вычислительную машину для проведения трансляции. Инструментальные средства, необходимые Бейкеру, поддерживались в нужном порядке кем-то еще. Один помощник проверял программы Бейкера и давал ему советы.
Ясно, что, поскольку работа выполнялась одним человеком, производительность сильно возросла.
Метод главного программиста прекрасно проявил себя в проекте для «Нью-Йорк таймс» и еще в нескольких проектах после него. Но у этого метода есть один недостаток. Главный программист может проделать такой объем работы, только если он в шесть — десять раз превышает средний объем. Этот метод тем не менее не подходит для больших проектов, программы которых составляют сотни тысяч строк программного текста.
Если объем программ меньше 100 тыс. строк, то метод главного программиста работает хорошо. Не так уж часто нам приходится работать над такими «небольшими» проектами. Ни разу нам не удалось добиться хорошей работы по методу «группы групп».
Из-за того что метод главного программиста был впервые применен в проекте «Нью-Йорк таймс», многие решили, что он является частью структурного программирования. Это не так!
Некоторые утверждали, что работы для «Нью-Йорк таймс» кончились неудачно. Но это не так. Мы (фирма IBM) сэкономили около 700 тыс. долларов, а система работала. Первое время в редакции «Нью-Йорк таймс» было слишком мало людей, которые имели опыт работы с системой. По мере подключения сотрудников газеты к работе в системе начались обычные проблемы перехода, которые всегда возникают при перекладывании ответственности за систему с группы разработчиков на группу сопровождения. Эти нормальные трудности были кое-кем неправильно истолкованы с целью бросить тень на великолепные результаты, полученные благодаря структурному программированию и использованию метода главного программиста.
Мне до сих пор досаждают вопросами вроде того, сколько же надо платить программистам, а я отвергаю эти вопросы с самого начала. Для оценки качества программ, создаваемых разными людьми в единицу времени, нужно учитывать по крайней мере 27 разных параметров. И прежде всего нужно определить, что представляет собой эта самая строка текста. Об этом мы поговорим в гл.6, посвященной производительности и ее оценкам.
Библиотекарь
Сравнительно новым методом, применяемым при разработке программного обеспечения, является использование библиотекаря. В прошлом программисты тратили очень много времени на то, чтобы перебирать колоды перфокарт, подготавливать распечатки программ, и на другие подобные мелочи. Вся эта канцелярщина теперь поручена библиотекарям.
Но дополнительным преимуществом их привлечения оказалось то, что все программы, написанные программистами, теперь бывают собраны в некотором централизованном доступном хранилище.
Почти неуловимое, но весьма важное изменение произошло в процессе разработки программного обеспечения. Программы стали собственностью организации, в то время как раньше они были собственностью программистов вплоть до тех пор, пока работа над ними не бывала закончена. Редко какой программист показывал своим товарищам все свои неудачные попытки заставить программу работать правильно. Если на это ушло девять пусков, кто об этом будет знать? Такая свобода порождала неопределенность в проведении процесса программирования. Она порождала — или по крайней мере сильно облегчала — поспешные решения, сильно ударяла по всей методологии выполнения работ.
Теперь руководитель работ может просмотреть легко читаемые, структурированные программы, которые вводятся в машину, и увидеть, сколько попыток сделал программист Джонс, чтобы получить работающую программу. Из частного дела программирование превращается в общественное.
«Библиотека» не имеет никакого отношения к библиотекарю. Библиотека — это собрание программ, обычно вспомогательных, которые могут найти применение в любое время.
Некоторые неправильно считают использование библиотекаря частью методики структурного программирования. Это разные понятия и методы.
На рис. 5.40 показаны и автоматический библиотекарь, работающий совместно с программами ведения диалога, и люди, которые были назначены библиотекарями для данного проекта.
Компоновка программ
Сборка большого числа модулей в одну работающую систему программного обеспечения
На этой фазе разработчики получают уже написанные программистом или группой программистов и даже до некоторой степени уже оттестированные программы и соединяют их в единое целое, таким образом, что вычислительная машина сможет выполнить сразу десятки или даже сотни программ, от самого начала до самого конца, получив в конце концов желаемый результат или выполнив желаемый процесс.
Я выбрал слово «компоновка» потому, что оно и в окружающем нас мире означает соединение заранее изготовленных частей в целое изделие. Именно этим мы и занимаемся при создании большой системы программного обеспечения.
Шесть фаз, выделенные нами в разработке программ, таковы:
Определение требований
Проектирование
Написание команд, программирование
Компоновка
Тестирование, или верификация
Документирование
Если нам нужна единственная, выполняемая сама по себе программа, фаза компоновки будет отсутствовать.
Причина, вынуждающая нас выделить компоновку в качестве отдельной фазы разработки программного обеспечения, состоит в том, что для такого рода деятельности в изобилии имеются новые автоматические средства. В прошлом подобная работа выполнялась вручную и, следовательно, очень медленно. В настоящее время в нашем распоряжении оказалось значительное количество программ, автоматически выполняющих компоновку систем программ из сотен подпрограмм.
Эти средства жестко связаны с выбранным ранее языком программирования, а иногда и с операционной системой, но все же стоят особняком от них.
Например, с языком программирования Ада, который, видимо, будет стандартизован министерством обороны, связан большой набор программ сопровождения, составляющих некоторое «окружение», которое могут использовать разработчики программного обеспечения для компоновки длинных цепочек программ. Это окружение носит название APSE (Ada Programming Support Environment — окружение сопровождения программирования на языке Ада), в сам язык встроены специфические конструкции и описатели, позволяющие обращаться к элементам окружения.
Между языком и его окружением в этом случае есть тонкое различие. Это различие весьма существенно. Если мне не надо вставлять программу в большую систему, я могу пользоваться только языком, не обращаясь к его окружению. Мне, следовательно, может понадобиться только транслятор, и я потребую, чтобы этот транслятор мог работать отдельно от всего окружения.
Следует ли проводить компоновку перед тестированием?
В нашем списке тестирование стоит на пятом месте, вслед за компоновкой. Но тестирование отдельных программ проводится часто и перед компоновкой. Под тестированием мы здесь понимаем тестирование системы, которое нужно выполнять только после компоновки.
Средства обслуживания руководства
Операционные системы помогают и программистам, и их руководству, и точно так же автоматизированные средства обеспечения разработки программ имеют многофункциональное назначение. Они также нужны и программистам, и руководителям разработки.
Мы уже видели, как они оказывают помощь при программировании. Теперь посмотрим на то, как они могут помочь руководству.
Когда под нашим руководством над созданием большого числа программ работают большие группы программистов, нам необходимо принимать во внимание несколько моментов:
1) взаимодействие;
2) распределение данных;
3) объединение;
4) выполнение собранных «кусков»;
5) отслеживание номеров версий и модулей.
Взаимодействие. Слово «заменимый» обозначает всем известное явление. Если я покупаю скобы к сшивателю бумаг, то, естественно, ожидаю, что смогу ими пользоваться. Это удается всегда. Удается потому, что их размеры, конструкция, форма, материал, вес и упаковка удовлетворяют некоторым стандартам. Такие скобы производят многие фирмы. Полная взаимозаменяемость скоб констатируется словами: «Скобы заменяемы». Взаимозаменяемы.
Этот же принцип имеет значение и в области программного обеспечения. Нам нужно установить образ действий, форму, синтаксические правила, словарь и необходимое содержание программы, которая будет работать совместно с другой программой. Если начать разговор о взаимодействии с определения всего этого, то программы можно будет объединить друг с другом со значительно меньшими затратами.
Распределение данных. Разные программы часто работают с одними и теми же данными. Все данные мы можем разделить на две очень разные группы — общие данные и данные приватные. Приватные данные принадлежат только одному модулю. Общие данные передаются в другие модули. Работа с данными по тому или иному способу имеет и свои плюсы, и свои минусы. Приватные данные делают модули более независимыми друг от друга, но такие программы труднее разрабатывать. Программы с общими данными разрабатывать легче, но такая методика может привести к тому, что один модуль повлияет на другой или уничтожит результаты его работы.
Автоматическая компоновка — объединение. Мы проектируем модули таким образом, чтобы их объединение проходило как можно легче и понятнее. Это дает нам возможность заменять ручной труд трудом автоматизированным с использованием программ, которые будут объединять модули автоматически. Раз наши правила ясны, объединение будет несложным делом. Процесс объединения имеет множество наименований:
Редактирование связей
Автоматическое построение
Интеграция
Объединение
Связывание
Между этими процессами есть некоторая разница, но все они очень похожи.
Выполнение собранных частей. Здесь мы снова сталкиваемся с мощными новыми средствами. В прошлом на многих существующих в настоящее время вычислительных машинах группы программного обеспечения вручную управляли системой программирования. Если у нас сотни модулей, такая работа отнимает больше всего времени.
К счастью, существует и продается множество программных пакетов, которые могут выполнять подобную работу автоматически. Они помогают пропустить на машине систему, которая получилась после выполнения процесса объединения.
Контроль версий системы. Для сборки, составления каталогов и хранения всех частей программной системы нами используются и вычислительная машина, и программы. Все модули имеют имена. Мы отдельно храним проверенные и готовые к работе системы, отдельно системы, проходящие тестирование, модули, прошедшие тестирование, и модули, не подвергавшиеся трансляции. Для автоматизации такого хранения, облегчения, ускорения и уменьшения вероятности возникновения ошибок мы опять прибегаем к помощи вычислительной машины. На рис. 5.41 изображен диск, на котором организовано хранение нескольких версий системы.
Автоматическое использование инструментальных средств
Собрание инструментальных средств это одно. Взаимосвязанный, автоматизированный набор инструментальных средств это совершенно другое. Еще в очень многих современных разработках программисты продолжают применять инструментальные средства вручную. Они вставляют в свою программу команды для вызова вспомогательных программ или физически вкладывают свои программы в тестирующие колоды и передают их на выполнение.
Сегодня программист уже имеет возможность оформлять обращение к этим инструментам отладки как вызовы процедур, а программное окружение, работающее под управлением операционной системы, выполнит задание, которое ранее приходилось выполнять вручную.
Недостаточно хорошие инструментальные средства значительно замедляют процесс разработки. Чем новее вычислительная машина, тем более непривычные средства предлагаются на ней, тем меньше средств предлагается вообще.
Выбор инструментальных средств должен следовать за выбором методики. Если сначала вы выберете язык, то это может помешать вам применять методы структурного программирования. Некоторые языки не отвечают методам структурного программирования.
Некоторые языки не дают вам возможности контролировать получающиеся модули. Некоторые лишают возможности упрятывания информации. Переходить к выбору инструментов нужно после выбора методик. А методика должна выбираться после того, как произведен выбор процесса. (См. рис. 5.42.)
Выбор трансляторов и языков
В основании выбора транслятора лежит выбор двух типов проводимой нами оптимизации, мы оптимизируем:
1) либо использование транслятора (время трансляции),
2) либо программу, получающуюся в результате трансляции (время использования). Техническая программа, которая будет выполнена только один раз, совершенно не нуждается в том, чтобы оптимизировать параметры ее использования.
Пользователь, который транслирует свою программу один раз, а затем ежедневно выполняет полученную в результате рабочую программу, совершенно не интересуется тем насколько быстро проходит эта трансляция; этот пользователь хочет иметь «эффективную рабочую программу». (См. рис. 5.43.)
Выбор языка. С такими же компромиссами приходится иметь дело и при выборе языка программирования. Какой язык вы будете использовать, зависит от задания, которое вам поручено, контингента ваших сотрудников и сроков, которые вам отведены. За выбором языка следует выбор транслятора, и язык должно выбирать руководство, а не программист (см. рис. 5.44).
Язык I (на рис. 5.44) оптимален в фазе разработки; язык II — в фазе сопровождения, но за счет времени первичной разработки.
Заметьте, что выбор языка не связан с выбором транслятора. А теперь посмотрите на схему последовательного выбора языка и транслирующей программы (рис. 5.45).
Реализация сверху вниз
Мы обращаемся к методу сверху вниз именно сейчас, а не при обсуждении процесса проектирования. Реализация сверху вниз смысл имеет, а вот проектирование сверху вниз совершенно бессмысленно.
В разделе, посвященном проектированию, мы увидели, что первой частью этого процесса является этап создания, творчества. По своей природе этот процесс связан с переходами взад и вперед. У этого процесса может быть верхний уровень, а может и не быть.
Но если мы можем найти этот верхний уровень в фазе проектирования, то реализовать программное обеспечение мы сможем, двигаясь сверху вниз. Мы можем создать программы верхнего уровня и, «отталкиваясь» от них, добавлять подчиненные программы, продвигаясь вглубь, уровень за уровнем, создавая нужное нам программное обеспечение.
Такой подход заметно отличается от метода реализации снизу вверх, когда первыми создаются программы самого нижнего уровня. Можно ли говорить в этом случае о проектировании сверху вниз? Многие считают, что это не проектирование, а реализация сверху вниз. Во многих сложных системах во многих случаях нужно проводить проектирование снизу вверх, поскольку система часто зависит от решений, принятых при проектировании одной-двух подсистем.
Окружение разработки программного обеспечения
При создании новой группы программ существенную помощь программистам и руководителям процесса разработки оказывают вычислительные машины и множество различных программ. В систему эффективно проводящейся разработки входят:
1) мощная вычислительная машина, на которой проходит разработка;
2) мощный и стабильный комплект программ, помогающий при программировании;
3) хорошая группа людей, составляющая и сопровождающая программы;
4) мощный набор программ, помогающих управлять разработкой.
Раньше для разработки нужны были помещения, люди, вычислительные машины и программы. Теперь разработка стала как никогда ранее автоматизированной и управляемой, и все это благодаря системе разработки — множеству программ, называемому окружением. Такое окружение — как написано применительно к языку Ада министерством обороны — состоит из: правил взаимодействия (интерфейса) с пользователем, базы данных и множества инструментальных средств.
Интерфейс с пользователем должен позволять разработчику программ управлять любыми инструментальными программами, а также вводить и выводить из системы информацию.
Базы данных могут быть разными — от простых справочников для пользователей до поддерживаемых с помощью вычислительной техники библиотечных систем. Инструментальные средства могут также быть разными — от листа бумаги и карандаша до автоматизированной системы тестирования, формирующей проблемно-зависимые тестовые данные.
Пользователь должен иметь возможность извлекать из базы данных сведения:
о доступных инструментальных средствах, о способах работы,
о доступных прикладных библиотеках, о том, чем различаются между собой версии инструментальных средств и программ.
Когда транслятор обнаруживает синтаксическую или логическую ошибку, он печатает сообщение об этом. Сообщение об ошибке, выдаваемое для разработчика программы, должно:
выдаваться развернутым текстом, а не каким-нибудь кодом,
указывать неправильную лексему, объект, действие пользователя,
указывать контекст, в котором обнаружена ошибка.
База данных должна удовлетворять современным стандартам на легкость использования и содержать:
информацию о самой себе, своей структуре и связях, все версии разрабатываемых программ, учебный материал по языку программирования и инструментальным средствам. В системе должны быть собраны сведения: об ошибках: их частоте и типах, о простоях, о денежных затратах, о графиках работ, о результатах тестирования,
об отчетах по использованию машин и программ. В систему должны быть включены модули (программы), работающие совместно с выполняемыми программами и похожие на модули обычной операционной системы. Примером могут служить программы стандартного ввода/вывода.
Инструменты, используемые при проектировании, это: справочники, стандарты разработки, руководства по разработке («рецепты»), анализ конкретных ситуаций проектирования с помощью вычислительных машин как в пакетном режиме, так и в диалоге.
Инструментальные средства должны:
помогать при интегрированном проектировании аппаратного и программного обеспечений, поддерживать структурную декомпозицию, включать графические средства,
быть интерактивными (работать в диалоговом режиме), по возможности использовать базу данных, включать методы моделирования и тестирования. Интерфейсы должны обладать следующими свойствами: быть стандартизованными, использовать соглашения о связях и загрузке, их трансляция должна обеспечивать взаимодействие с модулями непосредственной поддержки, в них должны учитываться средства тестирования и отладки.
Любой начальный набор инструментов и библиотек программ должен включать:
Транслятор.
Пакет программ непосредственной поддержки, в том числе пакет программ ввода/вывода.
Пакет программ работы с файлами.
Библиотечную систему для работы с модулями, написанными на языке Ада.
Редактор связей/загрузчик.
Набор средств отладки, работающих на символьном уровне. Оптимизатор.
Вспомогательные средства для составления толковых словарей, указателей и перекрестных ссылок.
Редактор текстов, ориентированный на входной язык.
Анализатор структур программ.
Средства преобразования.
В фирме IBM есть некоторый документ, описывающий подмножество такого окружения в несколько других терминах, но настолько похоже по существу, что о нем тоже стоит рассказать. В IBM это называется библиотеками поддержки разработок (Development Support Libraries).
Любая библиотека поддержки разработок представляет собой множество программ и процедур, которые обеспечивают:
1) постоянное отслеживание состояния дел, связанных с разработкой продукции;
2) руководство возможностью управления ходом работ по разработке этой продукции.
Библиотека поддержки разработки состоит из четырех элементов:
Внутренней библиотеки в виде, воспринимаемом машиной.
Внешней библиотеки в виде, воспринимаемом человеком.
Механизированных процедур и программ.
Канцелярских процедур.
Механизированная библиотека содержит все текущие сведения о программной части проекта, включая программы на входном языке, перемещаемые модули, операторы редактора связей, рабочие программы, операторы языка управления заданием, тестовые данные и связанную со всем этим информацию. Вся внутренняя библиотека печатается и хранится в удобном для человека виде во внешней библиотеке. Для ссылок хранится вся история предыдущих версий. Машинные процедуры состоят из стандартизованных управляющих операторов, которые выполняют следующие действия:
Редактирование библиотек.
Поиск модулей для трансляции и запоминания результатов.
Редактирование связей задач и организацию тестовых пусков.
Восстановление библиотек и возврат к предыдущим версиям.
Выдачу распечаток состояния библиотеки.
Канцелярские процедуры используются библиотекарями для:
регистрации изменяющихся состояний текстов внешней библиотеки.
Регистрации и замены листов в архивах. Программист работает только с внешней библиотекой.
Стыковка программ. Если наш процесс продолжается, переходя от человека к человеку, от машины к машине, от программы к программе, нам необходимо сопровождать его сведениями двух следующих типов.
1. Данные, или сырье, «полуфабрикаты». «Это материал, с которым нужно произвести известные вам действия. Приступайте к работе». (Данные.)
2. Информация о процессе. «Все надо делать вдвое быстрее». (Управляющая информация.)
Некоторые из таких пояснений могут достигать весьма значительных объемов. Пояснение данных выглядит просто, но оно может разрастаться, становясь тяжеловесным и слишком подробным. Управляющую информацию, возможно, и не удастся передать, придется просто начать с нее работу по следующему процессу, но в таком случае будет потеряна гибкость системы.
Есть и еще один весьма важный вопрос, который редко бывает поставлен с достаточной четкостью. «Когда нужно включать управляющую информацию?» При стыковке двух программ это может произойти на четырех разных этапах.
1. Во время трансляции. Если управляющая информация вводится на этом этапе, рабочая программа становится не соответствующей действительности. Для ее исправления надо провести перетрансляцию.
2. Во время редактирования связей. Эта часть этапа компоновки разрабатываемых программ влияет сразу на несколько раздельно транслированных программ. При этом достигается большая гибкость, поскольку перетрансляцию проводить не нужно, достаточно провести повторное редактирование связей.
3. Во время выполнения. Такое управление должно осуществляться непосредственно в вычислительной машине всякий раз, когда программы передаются на выполнение. Чтобы таким образом состыковать программы, нужно затрачивать и время процессора, и память, но гибкость системы значительно возрастает. Мы можем менять, модифицировать, приспосабливать программы во время каждого их выполнения. Для этого не требуется ни перетрансляции, ни повторного проведения редактирования связей. Такой метод позволяет достичь наибольшей гибкости.
4. Во время написания. Пишется программа, которая всего лишь транслирует другую программу, больших размеров. Это наименее гибкий метод; для внесения исправлений нужно переписывать программу.
Выводы
Все это говорилось вовсе не для того, чтобы навязать кому-нибудь систему, которую ему следует применять во время разработки. Просто хотелось подчеркнуть, что в настоящее время можно проводить разработку программного обеспечения, используя хорошо обеспеченные всем необходимым, обладающие хорошими эксплуатационными качествами системы разработки. Системы эти достаточно сложны, случайно возникнуть они не могут. Для того чтобы спроектировать и реализовать на практике систему, которая будет осуществлять действительно эффективную поддержку основных разработок, необходимы и время, и талантливые работники.
Строительство моста является результатом работы множества коллективов. Без организационной деятельности по управлению этими коллективами большой мост построить нельзя. Управление распределяет фонды, планирует, проектирует, нанимает, платит, строит графики работ, выдает директивы. Непосредственное руководство трудом рядовых сотрудников является частью этой многогранной деятельности.
Аналогичное положение имеет место и при разработке программного обеспечения, особенно при разработке крупномасштабного обеспечения. Руководство должно осуществлять все те же самые функции; исполнителями большей частью являются программисты, те, которые пишут команды. Перед руководством стоит двойная задача: ему надо не только воздействовать на увеличение отдачи от программистов, но также объединять результаты их труда в единое целое. Эта вторая часть сильно зависит от результатов проектирования, за которым она следует, и от методов, которые применяются при соединении частей системы. Именно это объединение составных частей мы и называем компоновкой.
На рис. 5.46 представлена роль автоматических средств обеспечения разработок, сильно возросшая в 1970-е гг., а также роль аналогичных работ, проводимых вручную, которая все это время неудержимо ослабевала. Этот процесс происходит только при хорошей организации работ по созданию программного обеспечения.
Верификация и тестирование
Верификация
Тестирование позволяет нам определить состояние того, что уже сделано, и показывает, на какое место надо обратить внимание и куда внести исправления. Верификация, прежде всего, призвана помочь нам избежать ошибок. Ошибки могут быть самыми разными — от неправильно закодированных операторов, которые могут приводить к остановке всей системы, и до чего-нибудь совершенно тривиального вроде ошибок при разбиении печатного текста на столбцы, из-за которых распечатка выглядит не так красиво, а ничто больше не затрагивается. Ошибки могут возникать из-за неправильной формулировки требований, неправильно проведенного проектирования, неправильного кодирования. В программах могут быть пропуски, или же туда может попасть лишнее. Причиной ошибок могут быть недоразумения, неоднозначные толкования, недоговоренности или путаница.
Верификация — это новый термин, он стал применяться около 5 лет назад. Обозначает он процесс проверки требований и проекта после того, как они документированы, а также разных частей программы по мере их появления. До того как возникла идея проводить верификацию, весь процесс выяснения того, отвечает ли программа сформулированным требованиям, откладывался до самого конца, до тестирования системы. Верификация передвинула процесс проверки ближе к началу, и ошибки и оплошности стали вылавливаться раньше, что дало возможность программистам избежать реализации неправильно сформулированных требований. Тестирование же обычно заключается в выполнении уже завершенных программ, чтобы убедиться, что они отвечают поставленным требованиям. Верификация выполняется с помощью инспекторских проверок или сквозного контроля. Коллеги, руководители, посторонние программисты, пользователи, если это возможно, собираются и просматривают результаты работы группы по определению требований (либо группы проектировщиков), пытаясь обнаружить в них пробелы, ошибки, неоднозначные высказывания и т. д. С каждым годом проведение верификации облегчается, поскольку методы формулирования требований и проектных решений становятся более четкими и легкими.
Раннее выявление «пробелов»
Сама идея тестирования предполагает некий «конечный продукт», который теперь надо протестировать и убедиться, что он работоспособен. Мы можем проверить, как работает автомобиль только после того, как этот автомобиль изготовлен.
Но очень хорошо известно, что если можно проверить отдельные части автомобиля еще до сборки, то машина более высокого качества будет получена с меньшими затратами. Своевременное выявление ошибок избавляет от них конечную продукцию или программу. Как же мы проверяем не готовую еще, не материальную продукцию? С помощью нового метода, который называется верификацией. Верификация устанавливает точность и правильность промежуточных результатов, документов, различных этапов процесса разработки. Сквозной контроль, который мы будем изучать позднее, тоже относится к процессу верификации. Частью верификации является и тестирование.
Тестирование есть деятельность, целью которой является установление того, что завершенная программа делает то, что ей надо делать. Лучшим тестированием любой системы является ее эксплуатация. В особенности это относится к системам реального времени. Однако слишком дорого обходятся ошибки и промахи, найденные слишком поздно. Мы желаем находить ошибки как можно раньше.
Вот в чем заключается деятельность группы тестирования:
понимании функций, которые должны выполняться системой,
обнаружении узких мест системы — временных ограничений и неустойчивости, вызванной входными параметрами, проектировании серии упражнений, приводящих систему в действие,
определении в тестирующей системе как аппаратных, так и программных компонент, построении тестирующей системы,
планировании фактического проведения тестирования и дирижирования им.
Для всего этого требуется и изобретательность, и широкие познания.
Тестирование есть сопоставление результатов разработки с процессом с целью выяснения, действительно ли разработанный процесс выполняет то, что предполагалось. Хотя речь о тестировании и верификации зашла в этой книге только сейчас, после разговора о компоновке, это связано лишь с необходимостью вести последовательное изложение. На практике правильнее будет провести большую часть действий по тестированию и верификации еще до компоновки. Мы включаем в систему тестирования все части программного обеспечения, по мере того как отдельные программисты и целые группы, работающие над нижними уровнями, объявляют об окончании своей деятельности.
Полезно будет еще раз взглянуть на рис. 5.7, иллюстрирующий процесс разработки. Заметим, что верификация и тестирование появляются уже на самых ранних стадиях проекта.
Часть усилий при проектировании следует направлять на то, чтобы сделать программное обеспечение легко тестируемым.
Инспектирование
Принятие методов структурного программирования привело к тому, что программы стали читаемыми. Сложившееся положение являет собой разительный контраст с прежними временами, когда неструктурированные программы больше напоминали бетонные монолиты или мешанину из спагетти.
Структурированные программы делятся на абзацы, при их написании следуют строгим правилам, в них включаются специальные примечания. Если к тому же размеры модулей невелики, не больше 30–50 операторов на модуль, мы получаем очень хорошо читаемые программы. Прекрасным способом проведения проверок на протяжении всего процесса разработки является инспектирование программ, называемое сквозным контролем.
Несколько коллег, а возможно, и руководителей, знакомых с проектом и применяемой терминологией, садятся рядом с автором программы, который показывает им все, что он написал, и описывает все, что он сделал: алгоритм своей программы, оператор за оператором, результаты всех тестов и т. д. Это новый метод; он стал возможен благодаря применению структурного программирования.
Если подобные инспекции проводятся регулярно, то в течение всего запланированного времени можно избежать любых, самых неприятных сюрпризов. Руководство становится более наглядным и простым.
Преимущества этого метода велики. Свежий взгляд позволяет избежать положения, когда «из-за деревьев не видно леса». Появились новые подходы и предостережения, позволяющие избегать ошибок.
Важным фактором становится «рабочая гордость», так как теперь видны не только результаты работы программы, но и каждая команда программы. Многие программисты сообщают, что там, где раньше они позволяли себе проявлять беспечность и небрежность при программировании (хотя никто из них этого раньше не замечал), теперь изо всех сил стараются делать все наилучшим образом. Повышенная аккуратность в работе приводит к повышению качества программ.
Член группы программистов учится у других членов своей группы, изучая их работу; он перенимает методы программирования, возможности языка, тонкости прикладной тематики. Постепенно пробелов в знаниях не остается. Инспекция программ должна стать неотъемлемой частью каждого более или менее крупного программного проекта. Подобные обзоры надо проводить не только для уже написанных программ, их следует регулярно осуществлять на всех стадиях процесса разработки. (См. рис. 5.47.)
Если разрабатывается особенно большая программная система и в производстве находятся сразу несколько модулей, следует одновременно проводить несколько сквозных контролей.
Если бы мы проводили сквозной контроль раньше, нам удалось бы избежать ошибки, которую мы допустили в одном космическом проекте.
Пример ошибки
Когда в 1965 г. приводнился корабль «Джемини V», мир затаил дыхание, ведь произошло это в 100 милях от заранее рассчитанной точки приводнения. Телевизионные комментаторы заставили весь мир поверить, что капсула может утонуть. К капсуле устремились авианосцы; через час после приводнения капсула была обнаружена вертолетами.
Из-за чего же могла возникнуть такая ошибка? Радиолокаторы, телеметрия, вычислительные машины работали отлично. Программы работали правильно и точно. Все дело оказалось в константе, необходимой для вычисления точки приводнения. Так как Земля вращается не только вокруг собственной оси, но еще и вокруг Солнца, то в одном из уравнений нужно подставлять полное время прохождения по орбите вокруг Солнца.
Спецификации для вычисления были правильные, однако программист решил пойти более короткой дорогой. Для вычислений следовало определить количество дней, часов, минут и секунд, прошедших со времени запуска, и, принимая Солнце в качестве неподвижного центра, рассчитать положение Земли в этой инерциальной системе. И все будет прекрасно. Программист выбрал более короткое решение и просто исключил из рассмотрения число дней полета (он разделил число часов на 24), «логично» рассудив, что по отношению к Солнцу Земля через каждые 24 ч оказывается в одинаковом положении. Это неправильно. Это было бы правильно, если Земля вращалась бы только вокруг собственной оси, а ведь она вращается и вокруг Солнца. Орбитальное движение вносит небольшие изменения в период возвращения Земли в ту же точку по отношению к Солнцу. Этот период не точно равен 24 ч. Этот «просчет» и привел к стомильной ошибке в определении места приземления.
Чья же это была ошибка? Конечно, программиста — он отклонился от проекта. Короткий путь оказался «заминированным». Но в самой программе ошибки не было; она была в формулировке решения. Сквозной контроль мог и, наверное, должен был привести к обнаружению ошибки. По крайней мере было бы обнаружено, что реализация не совсем точно следовала за проектом.
Тестирование и качество
Тестированию подвергаются все новые разработки. В Детройте имеются испытательные полигоны, на которых можно либо испытать новые автомобили, либо обнаружить скрытые пороки в его конструкции или проекте. Тестирование позволяет точно указать место ошибки, но повысить качество тестируемой системы оно не может. Качество должно повышаться процессом разработки и руководством.
Термин «качество» означает разные вещи для разных людей. Я понимаю под высококачественным обеспечением такое обеспечение, которое построено так, что в окончательной продукции находятся характеристики и фазы использования и фазы продолжающейся разработки. Напомним эти характеристики.
Заставляет машину выполнить действие |
Функция |
Занимает память машины |
Размер |
Тратит ресурсы центрального процессора |
Эффективность |
Легкость использования |
Практичность |
Легкость восстановления |
Восстанавливаемость/ Устойчивость |
Содержит ошибки |
Правильность |
Модифицируема |
Архитектура |
Существует по крайней мере в одной форме, а должна быть в двух |
Документация |
Тестирование помогает найти ошибку, но не избежать ее. Только хороший процесс разработки в сочетании с различными действиями по верификации и хорошо выполненный проект могут с самого начала воспрепятствовать возникновению ошибок. Система, состоящая из «заплаток», возникших при исправлении ошибок, редко оказывается понятнее системы, которая с самого начала не имела ошибок.
Если разрабатывается крупное программное обеспечение, мы сталкиваемся с проблемой многофункционального тестирования. Иначе говоря, если программы могут выполнять большое число функций да еще в большом числе разных последовательностей, мы должны попытаться оттестировать те из них, которые нам кажутся наиболее вероятными.
Важно, чтобы тестирование проводилось в широких масштабах, было интенсивным, ведь в большой, сложной программе может быть множество скрытых проблем и поворотов. Абстрактный характер программного обеспечения значительно затрудняет возможности увидеть эти проблемы до момента выполнения программы.
Тестирование систем типа V обходится очень дорого. Для тестирования системы диспетчеризации авиаперевозок в месте проведения тестирования в Атлантик-Сити, шт. Нью-Джерси, были собраны:
1) более 90 диспетчеров для работы с системой;
2) более 50 «пилотов», частично занятых местных сотрудников, имитирующих «полет» с помощью специальной аппаратуры, отсюда данные передавались на имитаторы радиолокаторов;
3) более 50 наблюдателей и советников;
4) более 50 работников вычислительного центра, обеспечивающих постоянную готовность программ и проч.
В распоряжении советников и диспетчеров имелись рукописи размером с большие телефонные справочники, в которых для каждого пульта (дисплея), каждой клавиатуры, печатающего и коммуникационного устройства секунда за секундой расписывалось все, что должно быть выполнено, показано на экране и т. д.
Тестирование длилось несколько недель со средней занятостью 250 человек в день. Но — и это «но» существенно — тестирование не закончено и до сих пор. Моделирующая система никоим образом не может дать той интенсивности, какую дает использование в реальной ситуации.
Число различных ветвей в этих больших программах и число возможных комбинаций состояний входных сигналов, данных, вычислений и взаимодействий настолько велико, что даже за 100 лет использования мы сможем начать выполнять лишь несколько процентов из всех возможных ветвей. Даже после нескольких лет реальной работы в программах могут сохраняться ошибки. Было известно, что в программах управления посадкой на Луну, написанных в Хьюстоне, есть почти 100 ошибок, но ни одна из них не была существенной.
Один высокопоставленный чиновник недавно даже приказал применить против некоторых системных программистов меры дисциплинарного взыскания, поскольку они твердили, что им никогда не удастся выловить все ошибки в большой системе программ. Конечно же, они не смогут этого сделать. Тот, кто продолжает вести разговоры о больших системах программ, свободных от ошибок, не понимает в программном обеспечении ничего.
После каждого теста системы диспетчерского контроля все расхождения, отмеченные в регистрационных книгах — «признак АА 222 не зажегся, хотя шум был слышен в 8:14:03 9 июля 1972 года», — проверялись, изучались, исследовались, создавались группы для определения причин. Относилось ли это к программированию? К аппаратуре? Методике? Может быть, это ошибка пользователя — может быть, диспетчер № 48 ввел неправильное число? Может быть, нарушена связь между машиной и системой отображения?
В течение нескольких месяцев в Атлантик-Сити была проведена целая серия проверок системы воздушного контроля. После завершения тестирования программы были поставлены в один из двадцати диспетчерских центров страны, и система работала там более восьми месяцев с полуночи до 8 ч. утра.
Только после этого, после исправления многих ошибок, после продолжительного тестирования, система стала использоваться во время тяжелых многочасовых дежурств в одном из центров.
Такой медленный изнурительный процесс необходим. Раньше мы уже говорили, что логическая сложность 600 тыс. строк программы была очень высока, а последствия ошибок были весьма тяжелыми.
Надежное программное обеспечение — неверный термин
Программное обеспечение не может ломаться! Оно может быть неправильным с самого начала, каким-то образом пройдя через тестирование, но сломаться, выйти из строя так, как это бывает с физическими устройствами, оно не может. Мир, для которого создавались программы, может измениться и потребовать чего-нибудь нового или другого (введен новый налог), но сломаться программа не может!
И все же люди говорят о «надежном программном обеспечении». Термин этот только наносит вред, он способствует неправильному пониманию природы программного обеспечения. Нам следует пользоваться выражениями «правильное обеспечение» или «высококачественное обеспечение». Это такое обеспечение, которое хорошо спроектировано, хорошо реализовано, хорошо оттестировано.
Устойчивым (робастным) может быть названо обеспечение, построенное так, что оно продолжает работать даже тогда, когда остальные части системы начинают действовать не так, как это планировалось. Когда вводится буква L там, где ожидалась цифра 1, программы не останавливаются, они начинают выяснять: «Вы ввели букву L, не хотели ли вы нажать клавишу 1? Эта процедура не рассчитана на ввод букв». Можно употреблять и термин «безошибочное обеспечение», но он имеет смысл только по отношению к небольшим программам.
Тестирование с возвратом
Как и все, что связано с вычислительной техникой, алгоритм тестирования целиком и полностью зависит от того задания, которое нужно выполнить, будь оно тривиальным или до крайности сложным.
Для проверки программы в 100 строк, которая вычисляет мой годовой доход, мне нужно составить всего несколько — вряд ли больше полудюжины — вариантов теста.
Для проверки программы размером в 1 млн. строк, предназначенной для управления ракетой, мне потребуются сотня или около этого помощников, целый год времени, ракетный полигон и огромное количество всяческой аппаратуры.
Давайте рассмотрим относительно простой пример — систему формирования платежных ведомостей, и на нем попытаемся понять идею тестирования с возвратом. Если разбить эту систему на составные части, получится нечто вроде рис. 5.48.
Все эти программы достаточно независимы, но все поставляют данные друг для друга. Изменения в программе вычисления выплаты, возникающие, например, из-за нового профсоюзного договора, должны быть проверены в совокупности не только с программой контроля выписанных чеков, но также с программой вычисления даты, с программой налогообложения и т. д. (См. рис. 5.49.) То, что программа правильно работает совместно с программой контроля чеков, не означает, что она будет верно работать вместе с другими программами. Необходимо выполнить несколько тестирований.
И наконец, нужно выполнить общесистемный тест (рис. 5.50) со всеми подпрограммами.
Даже если изменения вносятся только в одну программу, повторному тестированию подлежит вся система. Этот процесс называется тестированием с возвратом. Проверять работу только измененной подпрограммы недостаточно. Недостаточно полное тестирование такого рода повышает вероятность неудач.
Физическая сохранность
Не раз после всеобъемлющего тестирования и успешного прохождения всех тестов система на следующее утро уже не работала, потому что руководство проектом не обеспечило ее защиты. В одном крупном проекте, разработки которого проводило одно из отделений фирмы IBM в Австралии, руководители программистских коллективов решили, что они обязаны охранять работающие строки программ. Охрана программ должна проводиться и для больших военных проектов, и для внутрифирменных разработок операционных систем. От кого же? От жуликов? Нет, от чересчур усердных программистов!
Даже после проведения всех дорогостоящих тестов могут найтись рьяные программисты, которые захотят вставить в систему еще одну, последнюю функцию или исправить одну маленькую ошибку в своей подпрограмме и в три часа ночи, без всякого разрешения, прокравшись в машинный зал и схватив основной диск с оттестированной системой, внести туда изменения. И пустят маленький тест для проверки своих исправлений. Этот тест проходит — для их подпрограммы.
А в девять утра «оттестированная» система на ленте или диске будет передана на использование или демонстрацию — и вот в ней начинают обнаруживаться десятки разных отказов во многих других частях. Она не работает! Но еще прошлой ночью все было в порядке! Руководство очень быстро сообразило, что весь возглавляемый им весьма дорогостоящий процесс был нарушен — хотя и без злого умысла, но с ужасными результатами. Главные файлы должны храниться под замком с соблюдением всех мер безопасности.
Для тестирования обычно требуются очень крупные денежные средства. Проведение тестирования требует крупных и дорогих машин и программ. Это могут быть те же самые машины, что использовались и для разработки, но могут быть и другие. Все зависит от того, насколько велика система, является ли она аппаратно-интенсивной или программно-интенсивной. Если система достаточно велика, нам придется использовать для разработки и тестирования разные вычислительные комплексы (см. рис. 5.51).
Тестирующий комплекс проверяет не только новые или изменившиеся программные модули, но также все изменения в аппаратуре радиолокаторов, датчиков, линий связи, дисплеев и т. д. со всем существующим и оттестированным программным обеспечением.
Тестирование — средство обучения
В отделениях тестирования работают очень квалифицированные, всесторонне развитые люди. Тестовики должны уметь видеть всю систему, ее цели, структуру, иначе проверить ее нельзя. Лишь очень немногие среди людей, работающих в большом проекте, еще обладают таким кругозором. Остальные строят отдельные части, и лишь немногие проектировщики, руководство и занятые в тестировании видят целое.
При тестировании происходит целеустремленный поиск слабых мест, а для этого нужно проштудировать всю систему. Это хорошая тренировка. В течение многих лет мы замечаем, что люди, начинавшие с тестирования, становятся проектировщиками и руководителями новейших систем. Мы всегда преднамеренно поручаем тестирование очень способным молодым программистам, поскольку знаем, что уклонение от тестирования при реальном использовании приведет систему к краху. Лучше потерпеть крах до реального использования!
Независимость тестирующих групп
Тестирующая организация должна быть независимой, насколько это только возможно, ее отчеты должны направляться по цепочке руководства как можно выше. Если руководитель тестирования подчинен руководителю разработки, сразу начнут сказываться человеческие слабости — руководитель группы тестирования может сильно волноваться, критикуя систему, ведь критикуемым окажется его начальник.
Продолжительность развития
Случается, что после выпуска первой версии большой программной системы тестовые данные, ленты с тестами, результаты тестирования, сами тестовые программы выбрасывают за ненадобностью. Следует помнить, что группа сопровождения должна будет повторно проводить весь цикл тестирования.
Выводы
Судьба разработки большой программной системы решается в фазе тестирования. Конечно же, мы заранее знаем, что нам никогда не удастся полностью проверить систему, но мы также знаем и то, что чем больше мы проверим, тем лучше система будет работать впоследствии.
В работе по проверке программного обеспечения мы можем следовать следующим ориентирам.
Планирование тестирования должно начинаться одновременно с началом работ по разработке системы. Тестирование никогда не должно прекращаться — и варианты тестов, и аппаратное обеспечение тестов, и группы тестирования должны сохраняться в продолжающейся разработке или сопровождении.
Изменения в одном модуле программы требуют тестирования как этого нового модуля, так и всех остальных. Это называется тестированием с возвратом. Самая последняя, проверенная версия системы программного обеспечения должна храниться под надежной охраной, взаперти. Для тестирования нужно выделять самых квалифицированных членов группы разработки (кроме того, тестирование это лучший способ обучения специалистов).
Если пытаться извлечь из тестирования большой системы типа V хотя бы минимальную пользу, оно становится весьма дорогостоящим. С помощью тестирования нельзя плохую программу превратить в хорошую, ее можно только улучшить.
При тестировании следует обращать внимание на граничные случаи, это необходимо, чтобы убедиться, что система работоспособна во всем диапазоне, для которого она проектировалась. При тестировании необходимо вводить в систему неправильные данные, исследуя затем полученные результаты.
Медленно развивающиеся процессы следует тестировать с помощью длительных прогонов (24 ч.).
Документирование
Люди не очень-то любят составлять документацию на созданную ими продукцию. В то же время документация — это самое важное из того, что они должны сделать! Некий превосходный программист спроектировал и написал программу определения орбитальных характеристик спутника. Он первым закончил программирование, все работало правильно, память попусту не тратилась. Программа была написана на Фортране и занимала около 4 страниц плотного фортрановского текста. Он знал свою программу вдоль и поперек. Через 3 мес. его попросили добавить к своей программе несколько функций. Он достал документацию и принялся ее изучать. Три или четыре дня он пытался понять, что же происходит в его программе! А ведь он ее сам написал! Сколько бы сил он потратил, если бы это была чужая программа! А теперь представьте, что документации не было.
С такой проблемой часто приходится сталкиваться тем, кто занимается продолжающейся разработкой программ. Вместо того чтобы начинать с изучения требований к программе и ее проекта, им слишком часто достается участь детективов и археологов. «Какая была форма — структура — план — путь?» Почему «они» сделали «это»? После этого начинаются исправления, исключения, добавления. Понятно, что чем более четкий оставлен след, тем проще задача, тем лучше результаты.
Наверное, наиболее распространенная в мире история о программировании — это рассказ о том, что программа работает, но ни один человек не знает, как это происходит, поскольку программист давно ушел, а документации нет. Эту историю часто рассказывают, потому что это весьма обычная ситуация.
Документация нам нужна для того, чтобы:
1) напомнить тем, кто создал эту программу, о том, как она устроена;
2) показать тем, кто придет после нас, как устроена программа, каков ее алгоритм.
Самодокументирование
Многие новые технологии программирования позволяют получать самодокументированные программы, значительно снижая усилия, необходимые для документирования.
Под документацией мы подразумеваем не тексты рабочей (машинной) программы. Недостаточно также и текстов исходной программы. Документация, подготавливаемая для фазы продолжающейся разработки, должна содержать:
1) хорошо прокомментированный текст программы;
2) схемы, иллюстрирующие проект, и словесное их описание;
3) структурированные словесные описания или схемы процессов, причем первое более предпочтительно;
4) описания данных.
Без всего этого программисту, которому предстоит модифицировать существующую программу, придется решать сложнейшую задачу по определению того, что было сделано до него.
Структурированное словесное описание
Такие описания и читать и понимать легче, чем блок-схемы. Снабженный подробными приложениями, грамотно оформленный, разделенный на модули текст описания усваивается особенно легко, ведь и общая структура текста, и используемые в нем термины вполне понятны любому. Структурированное словесное описание это не программа, но эти понятия близки.
PROCEDURE: PWARN $ display warning message
INPUTS:
LOTMP — time to retry after overtemp condition
LEMI — time to retry after EMI condition
LACT — printer queue overflow flag
OUTPUTS:
none
IF LACT set THEN
IF NOR less than TQR (space unavailable) THEN
clear LACT
set LCODE to 134
call procedure POPMG to display operator message
ENDIF
ENDIF
DO-WHILE LACTX is 0 thru 1
IF EMI is set and CSRTC is greater than or equal to LEMI(LACTX) THEN
set LCODE to 321
call procedure POPMG to display warning message
store the new retry time in LEMI
increment retry counter LRCTR
IF LRCTR greater than or equal 3 THEN
clear the FM! flan
ENDIF
ENDIF
IF OVRTMP is set and CSRTC greater than or equal LOTMP(LACTX) THEN
set LCODE to 320
call procedure POPMG to display warning message
store new retry time in LOTMP
increment retry counter LRCTR
IF LRCTR greater than or equal to 3 THEN
clear OVRTMP flag
ENDIF
ENDIF
END-WHILE
END PROCEDURE-OUTPUT INTERRUPT
Рис. 5.52. Структурированное описание на языке проектирования программ.
На рис. 5.52 приведено структурированное словесное описание, для которого использован язык проектирования программ PDL. Это описание эквивалентно нескольким блок-схемам, которые изображены на рис. 5.53. Словесное описание лучше блок-схем по следующим причинам:
1) оно располагается на одной странице, и его общая структура легко обозрима;
2) оно содержит больше информации, чем блок-схемы.
На рис. 5.54 приведено другое описание с гораздо большим числом комментариев.
PROC SESSION MANAGEMENT * THIS PROCEDURE MANAGES THE
TERMINAL INTERACTION WITH THE USER. LEGAL USER
COMMANDS ARE MOVE AND DELETE *
USE SESSION DATA
DO * PROCESS USER COMMANDS *
GET INPUT (COMMAND) * NEXT USER INPUT *
RUN INPUTCHECK (COMMAND, ERROR)
IF
ERROR = TRUE
THEN
PUT OUTPUT (ERROR)
ELSE * NO ERROR — PROCEED WITH PROCESSING *
IF * DETERMINE TYPE COMMAND *
COMMAND = MOVE
THEN * PROCESS MOVE COMMAND *
INCLUDE MOVE PROCESSING
ELSE * PROCESS DELETE COMMAND *
INCLUDE DELETE PROCESSING
Fl
Fl GET INPUT (SESSION ~ ON)
WHILE * KEEP PROCESSING INPUT COMMANDS AS LONG
AS SESSION ON INDICATOR IS ON (TRUE) *
SESSION ON = TRUE
OD
CORP
DATA SESSION DATA
*ABSTRACT DATA TYPES & COMMENTS *
ATAD
Рис. 5.54. Структурированное описание.
Служебные слова CORP, OD, FI и ATAD представляют собой «закрывающие скобки» для слов PROC, DO, IF и DATA. Если правила ясны, то документы такого типа читаются очень легко.
Строки типа «IF * ОПРЕДЕЛЕНИЕ ТИПА ПРИКАЗА *» необходимо еще будет переводить на язык, с которого можно осуществлять трансляцию, но уже на этом уровне совершенно ясно, что нужно делать. В совокупности с хорошим общим описанием проекта такого рода документация должна быть вполне достаточна для продолжающейся разработки. Документы этого уровня можно обрабатывать с помощью машин, но транслировать их в рабочую программу еще невозможно.
Документация для других целей
Знать, что происходит в программе, должны не только люди, занимающиеся сопровождением, но и многие другие.
— Оператору, человеку, нажимающему кнопки в машинном зале, нужно подробно рассказать, что, когда, при каких обстоятельствах делать.
— Пользователь, сидящий за терминалом, является одним из тех, для кого строилась система. Пользователь должен иметь достаточно информации о том, что, как, по чему, когда происходит в системе. Информация должна быть изложена на достаточно понятном уровне.
— Руководителям пользователей необходима документация нескольких разных уровней. Что система делает? Чего она не умеет делать? Что возможно? Легко? Трудно?
— Для управления ходом разработки руководители разработки должны регулярно получать отчеты о состоянии дел и результаты тестирования. Большая часть этих документов в конце концов выбрасывается, поскольку почти вся она подчинена текущему моменту.
— Руководство пользователей должно иметь возможность знакомиться и изучать планы реализации или ввода в эксплуатацию.
— Руководство пользователей должно периодически изучать планы с требованиями на исходные данные. Что нужно от пользователя, чтобы эти данные не устаревали и были правильными? Группа сопровождения должна иметь намного больше разной документации, чем первичные разработчики, ведь ей приходится и модифицировать, и исправлять систему.
Отслеживание связей
Если взглянуть на заднюю панель любой очень большой вычислительной машины, станет очевидно, что нам совершенно необходима схема или хотя бы список, содержащий сведения о каждом проводке из точки XYZ в точку QLR. Это же относится и к программному обеспечению. Рассматривая каждый модуль как отдельную схему, можно сообразить, что нам потребуется отслеживать, кто, что и для кого делает. Но визуально представить себе программу труднее чем электронное устройство. Что такое модуль? В лучшем случае это наименьшая отдельно транслируемая часть программы, но такое толкование крайне изменчиво и в большой степени зависит от авторов. Один модуль может выполнять несколько функций. Для управления нашей большой программной системой нам нужно иметь таблицу привязки функций к модулям, и наоборот. Часто оказывается полезной схема вроде представленной в табл. 5.5 (см. также рис. 5.55)
Изучим теперь каждый столбец в табл. 5.5 и посмотрим, о чем же они нам рассказывают:
Столбец 1. Описание функции. Насколько это возможно, оно должно говорить само за себя.
Столбец 2. Номер модуля. Пользуясь этим номером, можно получать доступ к описанию модуля, тексту исходной программы и даже к рабочей программе, получающейся из модуля.
Столбец 3. Автор программы.
Таблица 5.5.
Описание функции |
Номер модуля |
Автор |
Данные поступают от |
Данные готовятся для |
Вызывает |
Вызывается |
Программа |
848А |
Дэниэлс |
848А |
437 849 Печати |
849 ОС Планировщик |
|
Программа печати чеков |
852 |
Шварц |
849 |
Нет |
Возврат |
831 |
Расчет профвзносов |
857 |
Трэверс |
839 |
858 |
858 |
852 |
Калькуляция |
1612 |
Уард |
442 857 |
894 1631 |
1614 |
1610 |
Печать калькуляции |
1614 |
Уард |
1612 |
Нет |
Возврат |
1612 |
Столбец 4. Какие модули или устройства формируют данные, используемые в данном модуле; имеются в виду непосредственные источники, а не вся их совокупность.
Столбец 5. Какие модули или устройства получают данные от данного модуля.
Столбец 6. Каким модулям может передаваться управление из данного модуля.
Столбец 7. Каким модулем вызывается данный.
Подобная автоматизированная система позволяет упорядочить и отрегулировать процесс обнаружения и устранения ошибок. Если размеры системы доходят до нескольких сотен тысяч строк текста программ и больше, число модулей и функций может достигать не одной тысячи. Автоматизированная система становится совершенно необходимой.
К счастью, в настоящее время уже существует множество инструментальных программ, помогающих нам проводить отслеживание требуемой информации, обеспечивающих легкий доступ к ней и возможности модификаций. И опять при разработке программ приходят на помощь вычислительные машины.
Избыток документации
Документация должна в точности соответствовать тем целям, для которых создавалось программное обеспечение. Если программы будут работать на 500 машинах, на борту каждого корабля ВМФ США, их надо документировать более тщательно, чем программы, используемые лишь однажды и затем выбрасываемые. Документацию для этих последних программ можно составлять на оборотной стороне конверта.
Если пользователей у программы нет, составлять пользовательскую документацию не следует. Звучит это довольна глупо, но почему-то часто все-таки случается. В «Стандартах» отмечена необходимость пользовательской документации, т. е. документов, в которых говорится о том-то и том-то, поэтому руководство требует, чтобы эти документы составлялись. И вот создается двухметровая кипа документов, ни один из которых ни разу не будет использован.
Исключение блок-схем
Блок-схемы больше не считаются необходимыми при составлении документации, за исключением самых высоких уровней. Готовить их трудно: в клеточки блок-схем можно вписывать весьма ограниченные сведения, занимают блок-схемы много страниц, они редко поддерживаются на уровне самых новых версий. Блок-схемы могут оказать полезную помощь при проектировании, но не при документировании.
История проекта
Занесение в документацию сведений о причинах принятия тех или иных решений при проектировании оказывается исключительно полезным, когда приходит время вносить в программы исправления. Почему они выбрали именно такое построение? Намеренно ли был выбран определенный путь решения определенных вещей? Почему? Если такие решения хорошо задокументировать, они помогут вносящим изменения избежать дорогостоящих ошибок. На этой стадии история проекта и заметки по решениям, принятым в проекте, могут быть очень полезными.
В общем должен еще раз подчеркнуть, что большие программные системы типа V требуют кучу документации, и так же как для эффективного проведения разработки программного обеспечения абсолютно необходима некоторая инфраструктура, так и для доводки и развития этого обеспечения необходима отличная документация.
«Как» — это «что». Требование — это проект — уровни детализации
Люди постоянно стремятся провести разграничительную линию между понятиями как и что. И это им никогда не удается, поскольку эти понятия едины и неразрывны. Все зависит от того, в каком отношении к этим понятиям вы находитесь.
Документы выполняют сразу две различные роли — во-первых, они нужны для управления разработкой, и, во-вторых, они нужны для управления продолжающейся разработкой, исправлением и использованием. В первом качестве они до некоторой степени опережают события, показывая, что предстоит сделать. С другой стороны, документация до некоторой степени «отстает», в ней фиксируется то, что уже сделано. По мере создания системы такая двойственная роль начинает доставлять все больше неприятностей. Посмотрим, почему это происходит.
После завершения нашей разработки мы получим целую иерархию документов, несколько их уровней, каждый из которых описывает систему все подробнее.
Такие иерархии документов существуют во всех технических отраслях. Министерство обороны установило весьма формализованные правила на составления спецификаций. В общих чертах схема таких спецификаций представлена на рис. 5.56.
По мере продвижения дел в нашем проекте наступает момент, когда мы получаем возможность написать спецификацию системы. Уровень детализации, с которым надо создавать эту спецификацию, весьма проблематичен. Какую же степень подробности нужно считать достаточной?
Чтобы проектировать новую сложную систему, нам нужно подробнейшим образом изучить некоторые важные аспекты этой системы, в особенности ее отличия от предыдущих, это необходимо, чтобы система могла функционировать. Но все эти детали не являются принадлежностью спецификации системы. И все же, поскольку они у нас есть, мы испытываем искушение включить их туда. Однако они чересчур подробны для описания столь высокого уровня. Они раздражают читателя, который не понимает да и не нуждается в таких деталях.
С другой стороны, что нам необходимо для утверждения спецификаций верхнего уровня? Нам может потребоваться большое число разных сведений по различным пунктам, не входящим в данный документ, но собранным в различных местах проекта. Нам могут понадобиться черновики будущих документов или детальные эскизы и описания, содержащиеся в рабочих документах, которые, конечно же, сосредоточены на более низких уровнях.
Еще раз повторяясь, можно сказать, что после достижения некоторого уровня детализации делаются попытки перенести его на документы более высокого уровня. В этом и есть ошибка. Документация высокого уровня должна быть свободна от этих подробностей. Для сопровождения спецификаций высокого уровня можно привлекать внутренние рабочие документы, но объединять документы не следует.
Требования должны содержать сведения о том, что является необходимым, а спецификация проекта должна описывать, как нужно этого достигнуть. Всегда нужно помнить, однако, что каждая спецификация есть одновременно и «как», и «что» — все зависит от того, с какой стороны вы на них смотрите.
Реальная ситуация
Необходимость документации совершено понятна. Столь же понятно и то, что часто не существует никакой документации — абсолютно никакой. Плохая документация — это одно, отсутствие документации — это совершенно иное. Существует столько программ без документации, что проблема кажется глобальной. Документация на программное обеспечение является более необходимой, чем документация на аппаратуру, ведь программы неосязаемы и абстрактны. Программа без документации похожа на невзорвавшуюся бомбу. В один прекрасный день она может взорваться.