жцпп. Лекция 1 и 2 этапы жизненного цикла. Основные этапы жизненного цикла по
Скачать 61.88 Kb.
|
Основные этапы жизненного цикла ПО (лекция по программной инженерии) Одним из основных понятий программной инженерии является понятие жизненного цикла ПО (software life cycle). Жизненный цикл ПО это период времени с момента принятия решения о необходимости создания ПО до момента его полного изъятия из эксплуатации. Жизненный цикл ПО состоит из определенных этапов. В соответствии с различными международными и национальными стандартами, регламентирующими этапы и процессы жизненного цикла ПО эти этапы могут носить различные названия. В данном курсе мы будем использовать следующие обобщенные названия этих этапов: Подготовка (сбор требований) Планирование (оценка, расписание) Моделирование (анализ требований, проектирование) Конструирование (кодирование, интеграция, тестирование) Развертывание (поставка, внедрение и сопровождение) Заметим, что часто под жизненным циклом понимают непосредственно процесс разработки в этом случае выделают такие этапы как: анализ требования. проектирование, кодирование, тестирование. Модели, описывающие разные варианты последовательностей этих этапов, будут рассмотрены в теме нашего курса «Модели жизненного цикла ПО». Организации работ над программным проектом в рамках моделей и в рамках каждого из этапов рассматривается в разделе курса «Управление программными проектами». Поясним суть каждого из этапов жизненного цикла. 1. Подготовка (сбор требований, анализ бизнес-процессов, разработка концепции, постановка задачи). На этапе подготовки осуществляется активное взаимодействие между фирмой-разработчиком ПО и фирмой-заказчиком, заключается контракт, собираются и формулируются требования, определяющие характеристики и функции разрабатываемого ПО. При разработке ПО этот этап исключительно важен. Ошибки, допущенные на этом этапе, даже при условии безупречного выполнения последующих этапов могут привести к тому, что разработанный программный продукт не будет соответствовать требованиям практики, сферы его применения. Для создания конкурентоспособных продуктов в ходе выполнения этого этапа должны быть получены четкие ответы на следующие вопросы: - Что должна делать программа? - В чем состоят реальные проблемы, разрешению которых она должна способствовать? - Что представляют собой входные данные? - Какими должны быть выходные данные? Ответы на вопросы этого этапа должны быть зафиксированы в документе, называемым «задание на разработку», который должен быть подписан представителями заказчика и исполнителя. Этот документ обычно содержит следующие укрупненные разделы: - общая характеристика задачи; - описание входных данных; - описание выходных данных; - описание алгоритмов решения задачи; - источники разработки. Рассмотрим эти разделы подробнее. В разделе «Общая характеристика задачи» должно как минимум три подраздела: назначение системы, экономическая эффективность и участники проекта. Назначение программы - это четкое представление о том, что зачем создается данная программа (это действительно необходимо подробно описать, так как часто заказчик и разработчик вкладывают разный смысл в одни и те же понятия). В разделе «экономическая эффективность» нужно определить насколько внедрение данной программы будет способствовать увеличению экономической эффективности конкретной фирмы, то есть насколько сократятся непродуктивные затраты пользователя после внедрения данной программы. Иногда экономическую эффективность можно выразить в денежном эквиваленте (например, суммированием затрат сотрудников, сокращенных в результате внедрения новой программы). Но чаще всего используют такие показатели эффективности как снижение времени на решение задач, уменьшение количества сотрудников, привлекаемых для решения задач, снижение накладных расходов. Чем полнее и ярче будет подана предполагаемая экономическая эффективность, тем легче будет продать программу. В разделе «Участники проекта» должны быть перечислены все подразделения заказчика, которые участвуют в решении задачи до и после внедрения программы. Желательно также указать графическое представление подразделений и связей между ними. Это очень облегчит дальнейший этап - определение спецификаций. В разделе «Входные данные» должны быть подробно описаны все документы, являющиеся источником данных для решения задачи. Учитывая структуру подразделений, участвующих в проекте нужно обойти их и собрать у сотрудников организации образцы всех таких документов. Если данные не документированы (например, телефонный звонок) сотрудник должен так или иначе дать их формальное описание. Пропуск хотя бы одного документа на данном этапе может привести к серьезным проблемам на этапе внедрения. Для каждого документа должна быть получена информация о его структуре, о том, откуда он поступает и с какой частотой. Если же программа разрабатывается не под конкретного заказчика и предназначена для автоматизации каких-либо реальных производственных задач, то для описания входных данных необходимо обратиться к экспертам. Причем желательно привлекать экспертов с реальных предприятий. Программы, которые проектируются на основе книжных описаний конкретной задачи, плохо продаются. Аналогично должны быть описаны выходные данные. Причем следует указать необходимость наличия физической копии. Например, некоторые данные можно хранить в базе данных и просматривать на экране, а другие нужно в обязательном порядке распечатывать. В разделе «Алгоритмы решения задачи» должны быть описаны не алгоритмы, используемые в разрабатываемой программе, а алгоритмы, которые используются для решения задачи на момент обследования. Возможно, это уже используемая устаревшая программа или ручная технология. В процессе обследования может быть выявлено, что некоторые документы, имеющие отношение к разрабатываемой программе, являются выходными для одного отдела и входными для другого. Именно такие документы должны «исчезнуть» в первую очередь после того, как программ будет внедрена. Необходимо абсолютно четко понять существующий алгоритм формирования выходной информации на основе входной. Для выяснения этого алгоритма не надо стесняться быть нудным и надоедливым. В разделе «Источники разработки» должна содержаться информация о том, откуда получены сведения, на основе которых описаны входные, выходные данные и алгоритмы решения задачи: фамилии и должности сотрудников, желательно даты бесед, законы, номера внутренних инструкция. 2. Планирование. На этапе планирования определяется объем работ, риск работ, необходимые трудозатраты, рабочие задачи план-график работ 3. Моделирование. Этап моделирования состоит из двух подэтапов: анализ требований и проектирование. Результат этих этапов обычно фиксируется в виде диаграмм и графических схем в документе, называемом «Техническое задание». В определенной степени анализ требований можно рассматривать как формулировку выводов, следующих из результатов этапа сбора требования. На этом этапе требования анализируются и формулируются в виде ряда строгих спецификаций, явно определяющих функции, интерфейс и рабочие характеристики разрабатываемого ПО. В число таких характеристик могут входить скорость выполнения, объем потребляемой памяти, гибкость применения и др. Данный этап должен завершиться составлением и подписанием документа «Техническое задание». , в котором, как правило, содержатся следующие разделы - название системы (полное и сокращенное название, версию); - цели создания; - характеристика области применения; - перечень автоматизируемых функций и требования к системе в целом (требования к соблюдению режимов безопасности, функциональной согласованности, к интерфейсу, степень взаимодействия с другими программами); - информационная база (все используемые файлы, их структура, организация взаимодействия пользователя с данными); - программное обеспечение (интегрированные среды программирования, СУБД); - аппаратное обеспечение (обычно указывается нижние пределы характеристик аппаратного обеспечения, но следует указать и оптимальную конфигурацию); - график работ. В некоторых разделах техническое задание похоже на задание на разработку, с тем отличием, что в техническом задании указывается не то, как данные обрабатываются в настоящей момент, а как они должны обрабатываться создаваемой программой. Основная задача проектирования состоит в разработки архитектуры ПО. Архитектура ПО определяет структуру ПС (задает ее разбиение на компоненты, связи между ними, их интерфейсы), а также основные принципы проектирования и развития системы. Современные программы разрабатываются на основе модульной технологии. Модуль это физически и логически независимая часть ПС. Физическая независимость подразумевает, что каждый модуль описан в отдельном файле. Логическая независимость подразумевает, что модуль решает логически объединенную между собой группу задач. В качестве модульной структуры программы принято использовать древовидную структуру, называемую модульное дерево ПС. В вершинах такого дерева размещаются программные модули, а стрелки указывают их подчиненность. В тексте модуля, из которого исходит стрелка, должна быть ссылка на тот модуль, на который она показывает. Другими словами, каждый модуль может обращаться только к подчиненным ему модулям. На этапе проектирования необходимо описать модульное дерево, то есть количество модулей, их подчиненность, для каждого модуля определить функциональность. Для каждого модуля должна быть составлено собственное описание – спецификация. Сложно ввести стандарт на степень детализации спецификации модуля, но обычно придерживаются следующих рекомендаций. В спецификации модуля приводится только интерфейсная часть. Можно высказать пожелания к общим типам данных и классов объектов, которые могут быть доступны из других модулей. Если описываются спецификации на подпрограммы, в них указывает назначение подпрограммы, а также количество и тип формальных параметров, их порядок, тип возвращаемого значения для функции. Кроме того, следует указать изменение изображение на экране или вывод печатного документа, который может произойти в результате вызова подпрограммы. Существуют различные методы разработки структуры программы. В процессе ее создания модульная структура может по-разному формироваться и использоваться для определения порядка кодирования. К двум основным методам разработки структуры программы относят метод восходящей разработки («снизу вверх») и нисходящей разработки («сверху вниз»). Существуют различные методы разработки структуры программы. В процессе ее создания модульная структура (т.е. структура модулей – компонент программы) может по-разному формироваться и использоваться для определения порядка кодирования. К двум основным методам разработки структуры программы относят метод восходящей разработки («снизу вверх») и нисходящей разработки («сверху вниз»). Метод восходящей разработки заключается в следующем. Сначала строится модульная структура программы в виде дерева путем описания всех режимов работы программы. Затем поочередно программируются модули, начиная с самого нижнего уровня. Для каждого программируемого в данный момент модуля должны быть заранее созданы те модули, к которым он может обращаться. После того как все модули созданы, производится их поочередное тестирование и отладка в том же порядке, в каком велось их программирование. При восходящем методе разработки модулей нижнего уровня глобальная информация для них может быть еще не окончательно определена, поэтому приходится перепрограммировать их позже, если при программировании других модулей производилось существенное уточнение глобальной информации. Метод нисходящей разработки заключается в следующем. Так же как и в предыдущем методе сначала строится модульная структура программы в виде дерева. Затем поочередно кодируются модули, начиная с самого верхнего уровня (головного модуля). К программированию очередного модуля переходят только после того, как запрограммирован модуль, который непосредственно к нему обращается. После того, как все модули описаны, производится их поочередное тестирование и отладка в том же нисходящем порядке. При этом те модули, к которым обращается данный, заменяют заглушками. Каждый модуль-заглушка представляется простым программным фрагментом, основная задача которого - сигнализировать о факте обращения к имитируемому модулю. После завершения тестирования и отладки модуля переходят к тестированию одного из модулей, которые пока представлены заглушками. При этом модуль-заглушку заменяют «полным » модулем, добавляя заглушки в те модули, к которым он обращается в ходе тестирования. Каждый модуль на момент обращения к нему при тестировании имеет «естественное» состояние данных. Таким образом, нисходящий метод позволяет заменить большой объем «отладочного» программирования программированием достаточно простых заглушек используемых модулей. Такой порядок разработки дает возможность своевременно формировать все необходимые глобальные переменные. У данного метода также есть недостаток – необходимость абстрагироваться от базовых возможностей используемого языка, придумывая абстрактные операции, которые позже необходимо реализовывать в модулях нижнего уровня. Кроме классических методов нисходящей и восходящей разработки, в которых модульная структура программы должна быть разработана до начала кодирования, применяют также подходы, при которых модульная структура формируется в процессе создания модулей: конструктивный и архитектурный. Конструктивный подход к разработке программы представляет собой модификацию метода «сверху вниз», при которой модульная структура программы формируется непосредственно в процессе программирования модулей. В процессе программирования головного модуля (исходя из спецификации всей задачи в целом) выделяются подзадачи. Для каждой выделенной подзадачи создается описание реализующего ее фрагмента программы, который в дальнейшем может быть представлен некоторым поддеревом модулей. Ответственность за выполнение выделенной функции несет головной модуль этой ветки. Аналогичные действия производятся при создании любого другого модуля, выбранного из текущего состояния дерева подпрограммы. Архитектурный подход к разработке программы – это модификация метода «снизу вверх», при котором модульная структура формируется в процессе программирования каждого модуля. При этом на первый план выходит другая цель: повышение уровня используемого языка, а не разработка конкретной программы. То есть для данной предметной области выделяются типовые функции, каждая из которых может использоваться при решении различных задач в этой области, и затем программируются модули, выполняющие эти функции. Процесс выделения таких функций связан с накоплением и обобщением опыта решения задач в заданной предметной области, поэтому обычно сначала выделяются и реализуются отдельными модулями боле простые функции, а затем постепенно появляются модули, использующее ранее созданные функции. Такой набор модулей создается в расчете на то, что при разработке той или иной программы заданной предметной области некоторые их этих модулей могут оказаться приемлемыми. Это позволяет существенно сократить трудоемкость разработки конкретной программы путем подключения к ней заранее заготовленных и проверенных на практике модулей нижнего уровня. Такие модули могут многократно использоваться в разных программах, поэтому архитектурный подход позволяет бороться с дублированием в программировании. 4. Конструирование. Конструирование ПО включает в себя кодирование, интеграцию и тестирование. Кодирование заключается в переводе на язык программирования конструкций, записанных на языке проектирования. Правила оформления текста программа см. в пособии Мирошниченко Е.А. «МЕТОДИЧЕСКИЕ УКАЗАНИЯ по организации и оформлению исходного текста программ» (Задание для самостоятельной работы.) К правилам указанным в этом пособии следует добавить замечание по нотациям (нотация – соглашение о правилах создания имен). В нотации Паскаля каждое слово, составляющие идентификатор, начинается с прописной буквы (MaxLength). Венгерская нотация отличается от предыдущей наличием префикса, соответствующего типу величины (iMaxLength). Согласно нотации Camel, с прописной буквы начинается каждое слово, составляющее идентификатор, кроме первого (maxLength). Отметим, что в C# для именования программных объектов чаще всего используют нотации Паскаля и Camel. Также распространена нотация, где между словами, составляющими идентификатор ставятся знаки подчеркивания (max_length), Интеграция программной системы – это процесс объединения отдельных (протестированных) модулей с целью получения системы, требуемой проектом. Тестирование, отладка и оптимизация. (В литературе часто этот этап просто называют тестированием). На этом этапе производится всесторонняя проверка программ, цель которой убедиться в том, что ПО является качественным. Существует стандарты качества ПО, один из которых (ISO/IEC 9126-1) будет рассмотрен в нашем курсе. На данном этапе пока определим качественную программу следующим образом. Качественная программа – это программа, выполняющая заранее объявленные действия известным способом и не выполняющая никаких необъявленных действий. Существует определенные методы тестирования, которые буду рассмотрены в разделе курса «Тестирование ПО». По поводу того как должно выполняться тестирование, среди разных программистов нет еденного мнения, но они единодушны в том, как тестирование не должно выполняться. Тестирование программы (или ее отдельных модулей) не должен выполнять программист (или группа программистов), создавший эту программу (модуль). Рассмотрим этот этап более подробно. Существуют три аспекта проверки программы на: - правильность; - эффективность реализации; - вычислительную сложность. Проверка правильности (верификация программы) удостоверяет, что программа делает в точности то, для чего она была предназначена. Статистика свидетельствует, что стоимость такого тестирования программного продукта составляет не менее 50 процентов стоимости начальной разработки и 70 процентов всей стоимости поддержки программного продукта. Но сколько бы сил и денег не было потрачено на тестирование необходимо понимать, что тесты могут доказать наличие ошибок в программе, но они не могут доказать их отсутствия. Один из общих законов программирования гласит, что ни одна программа не дает желаемых результатов при первой попытке ее трансляции и выполнения. На рисунке изображена диаграмма процентного соотношения причин появления тех или иных ошибок при обработке данных. Из диаграммы видно, что большинство ошибок совершаются именно на этапе кодирования. На следующем рисунке показа стоимость исправления ошибок на различны этапах ЖЦ. Существуют два типа программных ошибок: синтаксические и семантические. Синтаксические ошибки возникают из-за нарушений правил (лексики) языка программирования и выявляются во время компиляции. Такие ошибки могут быть исключены сравнительно легко. Большинство из них можно выявить путем простого просмотра текста программы. Чаще всего программисты оставляют этот этап компилятору, однако сквозной просмотр текста иногда бывает полезен. Скорость исправления ошибок, выявленных компилятора зависит от степени знакомства программиста с данным языком. Семантические или логические ошибки приводят к некорректным вычислениям или ошибкам во время выполнения программы (run-time error). Математическая безупречность алгоритма не гарантирует правильности его перевода в программу. Аналогично разумный вид получаемых результатов не дает достаточной гарантии правильности программы. В общем случае нельзя дать общего решения для проведения проверки на правильность программы. Конечно, самым лучшим способом тестирования программы является поставка ее пользователю сразу же после завершения программирования. Единственное слабое место такого метода – этот пользователь больше никогда не купит у вас не одной программы. Выявление и устранение ошибок часто имеет циклический характер. Устранение одной ошибки может породить другую ошибку. Особенно это касается работы с глобальными переменными. Как правило, устранение таких ошибок заключается в разработке и проведении наборов тестов, то есть выполнении программы с тщательно подобранными проверочными данными для которых известен правильный ответ. При подготовке к тестированию следует придерживаться следующих правил: 1. Чем раньше спроектирован тест, тем вероятнее выявление ошибок. Поэтому лучше готовить тесты еще на этапе проектирования системы. 2. Недопустима хаотичность процесса тестирования. Он должен быть документирован и полностью управляем. 3. Необходимы повторяемость и завершенность тестов. 4. Следует избегать добавления новых тестов в процессе тестирования. Первым шагом семантической проверки является ручной прогон, то есть программист моделирует прохождение данных через его программу с помощью карандаша и листа бумаги. Это скучная и утомительная работа, однако, большинство ошибок может быть выявлено именно на этой стадии. Разумеется, таким образом нельзя проверить всевозможные комбинации данных. Однако можно проверить всевозможные типы или наиболее вероятные комбинации данных. Если они дают правильные результаты, предполагается, что непроверенные комбинации также дали бы правильные результаты. Отдельно тестируется каждый модуль программы. Если программа хорошо спроектирована, то после этого остается проверить только интерфейс между модулями. Следует особо подчеркнуть, что первоначальное тестирование процедур модуля выполняется программистом путем написание коротких «программок», которые вызывают процедуры в различными наборами параметров. Начинающему программисту такой подход может показаться очень расточительным с точки зрения расходования рабочего времени, но это единственный способ более или менее эффективной борьбы с ошибками в программе. Очень часто тесты (файлы с тестовыми данными) создаются вручную. Следовательно, их число редко превышает два-три десятка. Иногда применяют генераторы тестовых данных – специальные программы, формирующие данные в соответствии со спецификациями задаваемыми программистами. Данные могут группироваться в записи, имеющие установленные форматы. Тестовые данные могут также систематически или случайно выбираться из другого заданного набора данных для уменьшения их общего количества, над которыми выполняется тест. Кроме того, необходимо на завершающем этапе тестирования следует прогнать программу на реальном объеме данных и посмотреть насколько интерфейс программы выдержит такую нагрузку. (Очень часто программа, прекрасно работающая для двух десятков записей в базе данных через год, когда база будет насчитывать сотни тысяч записей, будет выполняться несколько минут). Если прогон программы на тестовых данных дает неверный результат, то необходимо найти и исправить ошибку. При отладке программыиспользуют три основных способа: - распечатка текущего состояния - точка останова - трассировка Распечатка текущего состояния используется с целью фиксации фактических значений переменных для проверки хода вычислений. Для этого во время отладки программы в местах, которые программист считает критическими, помещают функции вывода на экран текущего состояния переменных. После окончания теста вызовы этих функций удаляются и программа снова перекомпилируется. Метод «точки останова» обычно применяют при разного рода зацикливаниях. В текст программы включают функции останова программы, например можно вывести на экран сообщение «Достигнута точка n» и вызвать функцию getch(). Этот метод имеет смысл объединять с методом деления пополам. Точка останова ставится в средине программы, если программа выполнилась до этой точки, что точка останова ставиться в средине второй половины программы, если нет, то в средине первой и т.д. Таким образом, область поиска ошибки постепенно сужается. Трассировка – это пошаговое выполнение программы с возможностью просматривать состояние всех переменных. Она может оказаться очень эффективной, но значительно замедляет выполнение программы и, не будучи тщательно спланированной, приводит к колоссальным объемам выдаваемой информации. Проверка вычислительной сложности, как правило, заключается в экспериментальном анализе сложности алгоритма или экспериментальном сравнении двух алгоритмов и более, решающих одну и ту же задачу. Проверка эффективности реализации (оптимизация) направлена на отыскание способа заставить правильную программу работать быстрее или расходовать меньше памяти. Теоретически, оптимизация не является обязательным условием разработки программы. Множество программ может быть поставлено (и поставляется) сразу же после отладки. Однако существует целый ряд программ, критичных как к скорости выполнения, так и к размеру. К таким программам относятся, например, программы графического вывода в силу большого объема вычислений, связанных с графическими преобразованиями. Очевидно, что оптимизация должна проводиться опытными программистами, хорошо знакомыми с тонкостями языка и компилятора. Поэтому сегодня чаще оптимизируются только фрагменты программы, влияющие на скорость вывода на экран изображений. При проектировании больших систем оптимизацию производится в два этапа. Сначала оптимизируется текст программы на языке высокого уровня, а затем наиболее критичные ко времени выполнения процедуры переписывают на языке ассемблера. Чтобы улучшить программу, пересматриваются результаты реализации в процессе построения алгоритма. Не рассматривая все возможные варианты и направления оптимизации программ, приведем здесь некоторые полезные способы, направленные на увеличение скорости выполнения программ. |