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

Костюк - Основы программирования

.pdf
Скачиваний:
134
Добавлен:
30.05.2015
Размер:
1.3 Mб
Скачать

151

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

Восходящая технология тестирования. Тестирование по методу снизувверх начинается, когда проектирование и программирование всех модулей системы завершено и для каждого модуля определены входы и выходы.

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

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

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

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

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

152

упрощенного варианта алгоритма, решающего ту же задачу, которую и должен ре­ шать модуль, но, возможно, не в полном объеме.

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

Тестирование по методу сверху-вниз обладает следующими достоинствами:

1)тестирование можно начинать почти сразу же после того, как началось проек­ тирование, это не только экономит время, но и позволяет программисту полностью сосредоточиться на модуле, пока тот не будет полностью отлажен, т.е. облегчает саму отладку модулей;

2)при проектировании и тестировании очередного модуля уменьшается вероят­ ность ошибки в согласовании входов и выходов вызываемых им модулей, а при обна­ ружении такой ошибки ее легко исправить для модуля любого уровня;

3)с самого начала тестирования система выглядит как единое целое, что создает

упрограммиста уверенность в успешном завершении разработки.

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

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

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

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

153

Тестирование параметров системы. В техническом задании приводятся, как правило, основные характеристики, которым должна удовлетворять система. К ним относятся, в частности, предельный объем входных данных, максимальные потреб­ ности в оперативной и дисковой памяти для размещения самой системы и обрабаты­ ваемых данных, время обработки больших объемов данных и др.

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

Тестирование документации. Эксплуатационная документация пишется для пользователя, и она должна облегчить ему работу с системой. К ней предъявляются следующие требования:

1)документация должна быть понятной для пользователя, который, как прави­ ло, не является программистом;

2)документация должна быть полной, т.е. в ней должны быть описаны все функции системы, все варианты задания входных данных, все варианты представле­ ния выходных данных и т.п.;

3)документация должна быть точной, т.е. в ней не должно быть никаких откло­ нений от истины в описании функционирования системы и в представлении данных.

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

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

154

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

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

8.6 Организационные проблемы технологии программирования

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

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

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

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

155

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

Все это позволило Ф. Бруксу сформулировать закон, носящий его имя: «Если при выполнении проекта добавить исполнителей, то окончательный срок завершения проекта может только увеличиться».

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

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

граммиста, хотя отдельные программисты даже в коллективе показывают невероятно высокую эффективность работы.

Чтобы каждый программист мог применить свои способности в коллективе с максимальной эффективностью, Ф. Бруксом еще в конце 1960-х годов было предло­ жено коллективы программистов организовывать в виде бригад, в которых расписа­ ны индивидуальные роли каждого. Бригада создается вокруг главного программиста, ее численность, как правило, не должна превышать 15–20 человек. Основные прин­ ципы организации бригады:

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

2)любой из членов бригады работает в непосредственном контакте с одним или двумя коллегами так, чтобы в случае его выбытия работа по проекту не приостанав­ ливалась.

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

156

все копии должны регулярно обновляться, например, ежедневно. Инструментальщик отвечает за подготовку, освоение, а иногда и за создание вспомогательных программ­ ных инструментов, необходимых всем другим членам бригады. Тестировщик отвеча­ ет за тестирование системы и ее модулей. Литературный редактор отвечает за подго­ товку документации, доводит «до ума» описания, написанные другими программи­ стами. Среди других членов бригады, выполняющих, в основном, программирование и отладку отдельных модулей, выделяют специалиста по языку программирования, используемого при разработке, специалиста по СУБД или по другим специальным системам.

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

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

При планировании проведения работ по разработке системы следует учесть так­ же такие законы технологии программирования, как: «Первая система создается на выброс» и «Вторая система обладает эффектом второй системы». Рассмотрим, в чем смысл этих законов.

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

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

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

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

157

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

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

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

Вопросы и задания

1.Почему трудозатраты создания комплексного программного продукта на порядок больше, чем трудозатраты при разработке аналогичной по объему простой программы?

2.Каковы цели и задачи основных этапов разработки системы?

3.В чем состоит сопровождение программного продукта? Что такое жизненный цикл си­ стемы?

4.Какие документы относятся к эксплуатационной документации, и какие сведения должны быть в этих документах?

5.Какие документы относятся к документации сопровождения, и какие сведения должны быть в этих документах?

158

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

7.В каком случае целесообразно метод проектирования сверху-вниз сочетать с проектирова­ нием снизу-вверх?

8.Обоснуйте целесообразность следования перечисленным в начале раздела 8.4 правилам написания текста программы.

9.Какими качествами должна обладать программа, чтобы ее можно было считать модуль­ ной?

10.Какими способами можно объединять модули в программе?

11.В чем состоит структурное программирование?

12.Что такое программирование с защитой от ошибок?

13.Когда целесообразно применять таблицы решений и в чем состоит технология их исполь­ зования?

14.Составить таблицу решений для задачи вычисления площади треугольника по трем сторо­ нам (см. пример 1.2).

15.Как проводить доказательство правильности и вывод трудоемкости большой программы методом сверху-вниз?

16.В чем преимущества нисходящей технологии тестирования по сравнению с восходящей? В каких случаях, тем не менее, необходимо применять обе технологии?

17.Как тестировать трудоемкость программы?

18.Какие требования предъявляются к эксплуатационной документации, и в чем состоит ее тестирование?

19.В чем состоит закон Брукса, и чем обосновывается его справедливость? Как минимизиро­ вать негативный эффект закона Брукса?

20.В чем состоят основные принципы организации бригады программистов?

21.Как распределяются обязанности в бригаде программистов?

22.В чем состоят законы первой и второй систем в технологии программирования? Что сле­ дует предпринять для минимизации негативного эффекта от действия этих законов?

23.Для чего необходимо независимое тестирование и как его организовать?

Приложение А Краткие сведения о языке Паскаль

А.1 Синтаксис языка Паскаль

В приложении приведено краткое описание языка Паскаль, точнее, варианта язы­ ка, реализованного в системе Turbo Pascal® фирмы Borland. Приведено описание не всего языка Паскаль, а такого его подмножества, которого достаточно для записи рассматриваемых в книге алгоритмов. Синтаксис (конструкции языка) представлен в виде набора расширенных порождающих правил Бэкуса. Кроме того, приведены пояснения к правилам, объясняющие их смысл (семантику). Каждое из порождаю­ щих правил определяет некоторое понятие языка Паскаль. В правиле записывается, из каких понятий или символов языка Паскаль строится определяемое понятие.

Расширенные порождающие правила записываются в следующем виде:

1)каждое правило записывается с новой строки текста;

2)правило начинается с определяемого понятия, после него пишутся символы ::= (два двоеточия и равно, их надо читать «это есть»), далее идет собственно определе­ ние понятия;

3)определение понятия состоит из последовательности понятий (слов, взятых в угловые скобки < и >), слов Паскаля (например, begin, end и др.), символов Паска­

ля (например, +, /, * и др.), символов | (большая вертикальная черта, надо читать как

«или»), символов { } (большие фигурные скобки), символов [ ] (большие квадратные скобки);

4)если две части определения разделены символом |, то для конкретного вариан­ та определяемого понятия используется только одна из таких частей;

5)если часть определения взята в скобки { }, то она рассматривается, как единое

целое;

6)если часть определения взята в скобки [ ], то в конкретном варианте определя­ емого понятия она может отсутствовать.

Замечание. Слова и символы Паскаля в правилах записываются шрифтом, ко­ торый отличается от шрифта других слов и символов в правилах.

В любом месте текста программы на языке Паскаль можно вставлять коммента­ рии. Комментарий записывается как последовательность символов, взятая в фигур­

160

ные скобки {}. Символы в комментарии могут быть любыми, кроме фигурных ско­ бок. Комментарии служат для пояснений, на выполнение программы они не влияют.

Среди всех понятий языка понятие <программа> является главным. Его опреде­ ление:

<программа> ::= [program <имя> ;] [uses <список имен> ;] <блок> .

После слова program (если оно есть) записывается имя программы, а после uses – имена подключаемых к программе пользователя библиотек (если требуется). Таким образом, программа может состоять из блока и точки в конце.

<имя> ::= <имя> {<буква> | _ | <цифра>} | {<буква> | _ }

Понятие <буква> – это одна из букв латинского алфавита (от A до Z), причем

строчные и заглавные буквы не различаются. Понятие < цифра > – это одна из деся­ тичных цифр (от 0 до 9).

<список имен> ::= [<список имен> ,] <имя> <блок> ::= [<описания> ;] <группа операторов>

<описания> ::= [<описания> ;] {<описание типов> | <описание констант> | <описание переменных> | <описание процедуры> | <описание функции>}

<группа операторов> ::= begin <список операторов> end

<список операторов> ::= [<список операторов> ;] <оператор>

<описание типов> ::= type <имя> = <тип> | <описание типов> ; <имя> = <тип>

Задаваемое в описании типа имя становится названием нового типа данных. Это имя можно использовать в других описаниях типов и переменных.

<тип> ::= [^]<имя> | [^]<стандартный тип>[[<константа>]] | [^]<запись> | <файловый тип> | <процедурный тип> | [^] array [<размерность>] of {<имя> | <стандартный тип>[[<константа>]] | <запись>}

<стандартный тип> ::= <целочисленный тип> | <вещественный тип> | char | string | boolean