Шрифт:
Эффективное использование большинства интерактивных средств требует, чтобы работа производилась на языке высокого уровня, поскольку телетайп и пишущую машинку нельзя использовать для получения дампа памяти. С использованием языка высокого уровня легко редактировать исходный текст и делать отдельные распечатки. Вместе они действительно составляют пару отточенных инструментов.
Глава 13 Целое и части
Я духов вызывать могу из бездны. И я могу, и каждый может, Вопрос лишь, явятся ль на зов они?
ШЕКСПИР, КОРОЛЬ ГЕНРИХ IV
Среди современных кудесников, как и встарь, встречаются хвастуны: «Я могу писать программы, которые управляют воздушным движением, перехватывают баллистические ракеты, делают переводы по банковским счетам, управляют производственными линиями». На что есть ответ: «И я могу, и каждый может, но будет ли работать то, что ты напишешь?»
Как написать программу, которая будет работать? Как протестировать программу? И как объединить набор протестированных программ-компонентов в протестированную и надежную систему? Несколько раз мы уже касались соответствующих приемов, давайте теперь рассмотрим их более систематически.
Проектирование без ошибок
Защита определений от ошибок. Самые пагубные и неуловимые системные ошибки возникают из-за несоответствия допущений, сделанных авторами различных компонентов. Подход к концептуальной целостности, изложенных выше в главах 4, 5 и 6, непосредственно обращается к этим проблемам. Кратко говоря, концептуальная целостность продукта не только упрощает его использование, но также облегчает разработку и делает менее подверженным ошибкам.
Такую же роль выполняет детализированная трудоемкая работа по разработке архитектуры, подразумеваемая этим подходом. В. А. Высоцкий из проекта Safeguard, выполнявшегося в Bell Telephone Laboratories, говорит так: «Решающая задача — дать определение для продукта. Очень многие неудачи связаны именно с теми аспектами, которые не были вполне специфицированы». [1] Тщательное определение функций, тщательная спецификация и старательное избегание всех украшательств функций и полетов технической мысли — все это снижает количество системных ошибок, которые будут обнаружены.
Проверка спецификации. Задолго до написания всякого кода спецификация должна быть передана сторонней группе тестирования для тщательного рассмотрения полноты и ясности. Как считает Высоцкий, сами разработчики сделать это не могут: «Они не могут признаться, что не понимают ее, они будут счастливо прокладывать свой путь через пропущенные и темные места».
Нисходящее проектирование. В очень четкой статье 1971 года Никлаус Вирт формализовал процедуру разработки, годами использовавшуюся лучшими программистами. [2] Более того, его замечания, сделанные в отношении разработки программ, полностью применимы к разработке сложных программных систем. Воплощением этих замечаний является разделение создания систем на проектирование архитектуры, разработку и реализацию. Более того, каждая из задач проектирования архитектуры, разработки и реализации лучше всего может быть решена нисходящими методами.
Вкратце, метод Вирта определяет разработку как последовательность уточняющих шагов. Набрасывается примерное описание задачи и грубый метод решения, позволяющий получить основной результат. Затем определение изучается более пристально, чтобы увидеть, в чем отличие полученного результата от требуемого, и крупные этапы решения разбиваются на более мелкие. Каждое уточнение в определении задачи становится уточнением алгоритма решения и может сопровождаться уточнением представления данных.
В этом процессе выявляются модули решения или данных, дальнейшее уточнение которых может быть продолжено независимо от основной работы. Степень такой модульности определяет гибкость и изменяемость программы.
Вирт считает необходимым использование на каждом шаге нотации как можно более высокого уровня, чтобы выделить понятия и скрыть детали, пока не станет необходимым дальнейшее уточнение.
Правильно осуществляемое нисходящее проектирование позволяет избегать ошибок по нескольким причинам. Во-первых, прозрачность структуры и представления облегчает точную формулировку требований к модулям и их функций. Во-вторых, расчленение и независимость модулей помогают избежать системных ошибок. В-третьих, проект можно тестировать на каждом уточняющем шаге, поэтому тестирование моно начать раньше и на каждом шаге сосредоточиться на подходящем уровне детализации.
Процесс пошагового уточнения не означает, что в случае столкновения с какой-нибудь неожиданно затруднительной деталью не приходится возвращаться назад, отбрасывать самый верхний уровень и начинать все сначала. На практике это часто случается. Но становится значительно легче точно увидеть, когда и почему нужно отбросить весь проект и начать сначала. Многие слабые системы появляются в результате попыток сохранить скверный первоначальный проект путем разного рода косметических заплаток. Нисходящее проектирование уменьшает такой соблазн.
Я убежден, что нисходящее проектирование является важнейшей новой формализацией программирования за десятилетие.
Структурное программирование. Другой важный круг идей для разработки, сокращающих число ошибок в программе, исходит то Дейкстры (Dijkstra) [3] и построен на теоретической структуре Бёма (Boehm) и Джакопини (Jacopini). [4]
В своей основе подход заключается в разработке программ, управляющие структуры которых состоят только из циклов, определяемых такими операторами, как DO WHILE и группами условно выполняемых операторов, ограниченных скобками с использованием операторов условия IF…THEN…ELSE. Бём и Джакопини показывают теоретическую достаточность таких структур. Дейкстра доказывает, что альтернативное неограниченное применение ветвление с помощью GO TO образует структуры, располагающие к появлению логических ошибок.