Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
38
Добавлен:
23.03.2015
Размер:
792.06 Кб
Скачать

13.9. Обзор и обсуждение

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

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

В каждом случае абстракции создаются для сокрытия подроб­ностей. Например, абстракция docскрывает подробности формати­рования выходных данных, a doJineскрывает обработку и интер­претацию текста. Подобное «сокрытие» желательно по двум при­чинам. Это дает возможность управления модифицируемостью и сложностью. Мы структурируем задачу по степеням сложности, что позволяет ограничить число проблем на каждом шаге. Воз­можность модифицируемости позволяет нам изменять в дальней­шем мелкие подробности, не затрагивая других абстракций.

В процессе проектирования мы руководствуемся следующими факторами:

1.Знанием того, что абстракции уже доступны. Это включает в себя как абстракции, имеющиеся в языке программирования и в любой доступной библиотеке программ, так и уже определенные в процессе проектирования.

2.Знанием существующих алгоритмов и структур данных. Необходимо знать об уже существующих методах. Например, проектировщик должен знать об известных методах сортировки и поиска и не изобретать их заново.

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

Например, наше решение о построчном вводе-выводе основано на знании того, что однопроходные алгоритмы, как правило, менее эффективны с точки зрения используемого объема памяти, чем

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

Наибольшее влияние на проектирование оказывают используе­мые типы. Разумеется, процедуры и итераторы также важны, однако они скорее появляются не независимо, а как операции ти­пов. (В нашем случае в программе для форматировщика итераторы отсутствуют.) Мы определяем типы в четырех областях: вводе, выводе, внутренних структурах данных и индивидуальных эле­ментах данных. Проект для задачи formatсодержит примеры двух из этих типов. Docпредставляет собой абстракцию для вы­вода, a lineесть абстрактная структура данных. Абстракция слова могла быть и абстрактным элементом, но мы решили, что в этом нет необходимости. Однако в будущем может потребоваться аб­стракция команды, особенно в том случае, если пользователям будет разрешено создавать свои собственные команды.

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

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

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

296 Глава 13

шения работы программы (например, окончание потока входных данных). Мы можем обнаружить пропущенные аргументы или результаты или обнаружить, что аргументы или результаты имеют? неправильный тип. Для типов данных мы может обнаружить про­пущенные операции. Более того, если абстракция получилась слишком сложной, то мы можем обнаружить это при попытке написания спецификации. Сложная спецификация, естественно, приводит к попытке ее упрощения. Результатом обычно является более простая абстракция.

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

Мы еще не рассматривали вопрос о том, насколько подробным должен быть процесс проектирования. На каждом шаге анализ должен быть подробен до такой степени, чтобы дать возможность определить и специфицировать все вспомогательные абстракции. Большей детализации не требуется. Например, в абстракцииdo.nextJineмы не обсуждаем проблему сканирования, поскольку очевидно, что это легко может быть сделано при помощи операций с потоком. В противоположность этому проектирование абстрак­ции docпроводилось очень подробно. В действительности в избы­точных подробностях необходимости не было. Например, нам не нужно было анализировать подробности процесса создания стра­ниц. Если бы мы не делали этого анализа, то получить набросок представления, инварианта представления и функции абстракции было бы гораздо легче. В таких случаях на этапе реализации приходится выполнять больше работы (локального характера), например для определения в представлении абстракции docточ­ных значений для Нпепо и pageno,и, возможно, даже ввести и специфицировать внутреннюю процедуру outputJine.(Факти­чески, приведенные в приложении Б реализации docи lineис­пользуют дополнительные внутренние программы.)

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

': Проектирование

•требование изучения в первую очередь абстракций, находящихся

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

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

Например, предположим, что мы забыли передать аргумент отfrom_rightк line$justify.Эта ошибка может быть обнаружена в процессе принятия решения о том, что делать с дополнительными пробелами. Путем анализа диаграммы модульной зависимости мы можем обнаружить ту реализацию, которую необходимо пере­смотреть. Поэтому мы возвращаемся назад, исправляем ошибку (возможно, что мы забыли включить соответствующую информа­цию в представление абстракции doc),исправляем спецификациюjustifyи продолжаем работу с реализацией для line.

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

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

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

298 Глава 13

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

Отметим, что проектирование представляет собой процесс, распространяющийся сверху вниз; т. е. мы всегда решаем, каким образом достичь того, что мы хотим. Четко придерживаясь этой цели (требующейся нам программы), мы можем на пути к решению свободно использовать свою интуицию. Опыт написания программ развивает у программистов интуицию относительно того, что мо­жет быть реализовано и с какой эффективностью. Также расширя­ются представления о том, какие программные структуры под­ходят для решения различных задач. Эти знания оказывают су­щественное влияние на процесс проектирования сверху вниз.

Альтернативой проектированию сверху вниз является подход «снизу вверх». При этом разработчик отталкивается от того, что может быть реализуемо, двигаясь к тому, что должно быть реали­зовано. Подход снизу вверх удобен лишь для очень небольших программ. Для больших программ разрыв между доступным и желаемым весьма велик и должен быть устранен введением боль­шого числа абстракций. Этот разрыв удобнее ликвидировать через подход сверху вниз, поскольку мы можем сосредоточить свое вни­мание на том, что менее всего понятно (требуемая программа), и воспользоваться тем, что .нам уже известно. При проектировании сверху вниз мы .интуитивно чувствуем, двигаемся ли мы в пра­вильном направлении: вспомогательные абстракции легче реали­зовывать, чем целевую. При проектировании снизу вверх оценить успех работы гораздо труднее, поскольку труднее оценить, на­сколько мы близки к желаемому результату.

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

13.10. Заключение

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

! Проектирование

как производится эта декомпозиция, и проиллюстрировали про­цесс проектирования на примере. Также был рассмотрен метод документирования с применением рабочего журнала проектиров­щика.

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

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

Дополнительная литература

Bentley, Jon L., 1982. Writing Efficient Programs. Englewood Cliffs, N. J.: Prentice-Hall,

Hester, S. D., David L. Parnas, and D. F. Utter, 1981. Using documentation as a software design medium. Bell System Technical Journal 60 (8): 1941—1977.

Kernighan, Brian W., and P. L. Plauger, 1981. Software Tools in Pascal. Reading, Mass.: Addison-Wesley.

Parnas, David L., 1972. On the criteria to be used in decomposing systems into modules. Communications of the ACM 15 (12): 1053—1058.

Упражнения

13.1. Спроектируйте и реализуйте программу KWIC, спецификация которой приведена в гл. 12 и упражнении 1 гл. 12,

13.2. Разработайте и реализуйте программу xref, описанную в упражнении 2 гл. 12.

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

Соседние файлы в папке POSIBNIK