Методические указания к практическим занятиям по курсу «Надежность информационных систем» Для студентов, обучающихся по направлению 654700 «Информационные системы» icon

Методические указания к практическим занятиям по курсу «Надежность информационных систем» Для студентов, обучающихся по направлению 654700 «Информационные системы»



НазваниеМетодические указания к практическим занятиям по курсу «Надежность информационных систем» Для студентов, обучающихся по направлению 654700 «Информационные системы»
Дата конвертации27.02.2013
Размер259.13 Kb.
ТипМетодические указания

МИНИСТЕРСТВО ОБРАЗОВАНИЯ РОССИЙСКОЙ ФЕДЕРАЦИИ




ВОРОНЕЖСКАЯ ГОСУДАРСТВЕННАЯ ТЕХНОЛОГИЧЕСКАЯ АКАДЕМИЯ


КАФЕДРА МАТЕМАТИЧЕСКОГО МОДЕЛИРОВАНИЯ ИНФОРМАЦИОННЫХ И ТЕХНОЛОГИЧЕСКИХ СИСТЕМ


надежность проектирования и программирования модулей


Методические указания к практическим занятиям

по курсу «Надежность информационных систем»


Для студентов, обучающихся

по направлению 654700 – «Информационные системы»

специальности 071900 – «Информационные системы и

технологии» дневной формы обучения





ВОРОНЕЖ

2002

УДК 681.3.06


Надежность проектирования и программирования модулей: Методические указания к практическим занятиям по курсу "Надежность информационных систем" / Воронеж. гос. технол. акад.; Сост.: Ю.В. Бугаев, О.Л. Полушина. Воронеж, 2002. 23 с.


Методические указания разработаны в соответствии с требованиями ООП подготовки инженеров направления 654700 «Информационные системы» по специальности 071900 «Информационные системы и технологии». Они предназначены для закрепления теоретических знаний .

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


Библиогр.: 8 назв.


Составители доцент Ю.В. Бугаев,

ассистент О.Л. Полушина


Научный редактор профессор В.В. СЫСОЕВ


Рецензент профессор Д.Б. ДЕСЯТОВ


Печатается по решению

редакционно-издательского совета

Воронежской государственной технологической академии


 Бугаев Ю.В.,

Полушина О.Л.,

2002

 Воронежская

государственная

технологическая

академия, 2002

^ Цель работы: приобретение навыков по проектированию и программированию программных модулей, устойчивых к ошибкам.


1. Место проектирования и программирования модулей в обеспечении надежности информационных систем


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

  • появление на входе программы набора данных, принадлежащих непроверенной (непротестированной) области;

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

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

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

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

Можно выделить два типа таких воздействий:

  • некорректные данные;

  • внесение в программу изменений.

В идеале устойчивая программа в ответ на некорректные данные должна:

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

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

  3. встретив некорректный элемент, из-за которого вычисления надо прервать, проверить на корректность все остальные элементы.

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

  1. структура программы должна быть максимально приспособлена к модификации;

  2. программа должна быть написана максимально просто и понятно.

Пример 1.1. Задание: в списке L заменить первое вхождение элемента со значением ^ E на значение E+1.

Предлагается следующий вариант:

Алгоритм 1

Begin for uL do

if u = E then

begin u := E + 1;

Goto 10;

end;

writeln(‘Вхождений нет’);

halt;

10: for uL do write(u);

End;

Взглянем теперь на задание с более общих позиций. Изначально предполагается, что в списке ^ L несколько элементов могут иметь значение E. Требуется у какого-то одного из них (а возможно у некоторых), поменять значение E на какое-то другое. При такой интерпретации становится очевидным, что вполне ожидаемой является следующая модификация: в k-ом по счету элементе, со значением E поменять значение E на f(E), причем k совершенно не обязательно равно 1. Очевидно, что такая модификация потребует существенной переделки логики модуля. Следовательно, его предложенная структура весьма неустойчива к внешним изменениям.

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

Алгоритм 2

Function F(E);

begin

F := E + 1;

end;

Begin s := 0;

k := 1;

for uL do

begin

if u = E then s := s + 1;

if s = k then

begin u := F(u);

halt;

end;

end;

writeln(‘Нужного вхождения нет’);

End;

Очевидно, что в случае модификации условий задачи изменение коснется двух операторов модуля, а структура модуля не изменится.

Реализация основных принципов проектирования надежного ПО закладывается на нижнем уровне – проектирование и программирование модулей, составляющих ПО.

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



  1. ^ Проектирование программных модулей.

Структурное программирование


Процесс проектирования программных модулей состоит из двух этапов: проектирование внешних функций и проектирование логики.

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

  1. входные данные (точное указание структуры и диапазона);

  2. выходные данные (описание всех реакций системы);

  3. возможные изменения состояния системы при каждом виде входных данных с точки зрения пользователя;

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

  5. требования эффективности (время, память и т.д.);

  6. замечания по программированию (если у автора спецификации есть какие-то идеи по программированию, то их нужно записать).

Проектирование логики модуля (внутреннее проектирование) представляет собой следующую последовательность шагов:

  1. Проверка внешних спецификаций (они должны соответствовать информации о сопряжениях и подтверждаться анализом вызывающих модулей);

  2. Выбор алгоритма и структуры данных;

  3. Объявление данных из сопряжения;

  4. Объявление остальных данных;

  5. Детализация текста программы (начиная с верхнего уровня абстракции последовательно детализировать логику модуля, используя блок-схемы и неформальные языки);

  6. Осуществление ручной проверки программы;

  7. Тестирование модуля компиляцией.

Идея пошаговой детализации логики программных модулей составляет методологическую основу структурного программирования – одного из направлений в проектировании и программировании программных продуктов. Научные основы структурного программирования были заложены на рубеже 60 – 70-х годов в трудах Дейкстры и Вирта. Метод пошаговой детализации позволяет существенно упростить проектирование модулей со сложной логикой.

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

Алгоритм 3

Begin

for k := 1 to n – 1 do

begin m := номер допустимой строки;

if m<>k then строка[m]строка[k];

for i := k+1 to n do

преобразовать строку [i];

end;

End;

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

На следующем шаге детализации абстрактные функции «номер допустимой строки», «преобразовать строку», процедуру обмена строк () необходимо наполнить конкретным содержанием с расшифровкой механизма реализации каждой процедуры.

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

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

Часто структурное программирование называют программированием без goto. Это не совсем верно. Подобная легенда возникла, видимо, из-за того, что в первой работе по структурному программированию, опубликованной Дейкстрой в 1968 году, говорилось «о вреде оператора goto». Согласно изложенной в статье концепции, между текстом программы и последовательностью выполнения ее операторов должно быть простое соответствие, благодаря которому, читая текст программы, можно достаточно просто отслеживать выполняемые действия. Неограниченное использование оператора goto, особенно goto назад по тексту программы, серьезно нарушает это соответствие.

На самом деле можно привести массу примеров, когда наличие или отсутствие операторов goto не улучшает и не ухудшает программу. Можно дать следующее, более правильное определение структурного программирования, принадлежащее Майерсу [7]: это программирование, ориентированное на общение с людьми, а не с машиной. Чтобы удовлетворять этому определению, программа должна обладать следующими качествами:

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

  2. Операторов goto надо избегать всюду, где возможно;

  3. Текст программы должен быть написан с правильными сдвигами (лесенкой);

  4. Каждый модуль должен иметь ровно один вход и один выход;

  5. Текст программы должен быть физически разбит на части для облегчения чтения;

  6. Программа должна представлять собой простое и ясное решение задачи.

Цель структурного программирования – писать программы минимальной сложности, заставить программиста мыслить ясно, облегчать восприятие программы.

Пример 2.2. Функция модуля – выдать все возможные значения целочисленных переменных i, i=1…n, удовлетворяющих следующим условиям: , dN.

^ Неструктурный вариант программы:

Алгоритм 4

Begin

for i := 1 to n do i := 0;

s := 0;

10: n := d – s

writeln(1, 2,…, n);

k := 1;

20: k := k + 1;

s := s + 1;

if s  d then goto 10;

s := s - k;

k := 0;

k := k + 1;

if k  n-1 then goto 20;

End;

В этом варианте два раза используется goto назад.

Структурированный вариант программы:

Алгоритм 5

Begin

for i := 1 to n do i := 0;

s := 0;

repeat

if s  d then

begin

n := d – s

writeln(1, 2,…, n);

k := 1;


k := k + 1;

s := s + 1;

end else

begin

s := s - k;

k := 0;

k := k+1;


k := k + 1;

s := s + 1;

end;

until k > n-1;

End;

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


^ Контрольные вопросы и задания


  1. Написать структурный вариант алгоритма генерации чисел i (пример 2.2). За основу рекомендуется взять следующую идею. Пусть (k1, k2, … ,kn-1) – некоторое сочетание (n-1) элементов из d возможных. Тогда числа i и ki связаны следующим соотношением:

i = ki –ki-1, i=1,…,n-1; k0=0; (1)

n = d – kn-1.

Алгоритм генерации сочетаний можно найти в [6].

  1. Написать структурный вариант программы бинарного метода возведения целую неотрицательную степень m произвольного вещественного числа x. За основу взять следующий неструктурный алгоритм [4, т.2]:

Шаг 1. Начальная установка: N := m; Y := 1; Z := x.

Шаг 2. Установить N := [N/2] и одновременно определить, было ли старое значение N четно или нечетно. Если N было четно, перейти к шагу 5.

Шаг 3. Установить: Y :=Y * Z.

Шаг 4. Если N=0, то алгоритм закончен, ответ равен Y.

Шаг 5. Установить Z := Z * Z. Вернуться к шагу 2.

  1. Доказать соотношение (1).

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


Варианты заданий


  1. Дан однонаправленный список L^ 1. Включить в него перед вторым вхождением элемента с заданным значением E подсписок L2.

  2. Дан однонаправленный список L1. Включить в него перед предпоследним вхождением элемента с заданным значением E подсписок L2.

  3. Дан однонаправленный список L, элементы которого отсортированы по невозрастанию. Вставить в список k новых элементов, удаляя каждый раз последний элемент, чтобы упорядоченность сохранялась.

  4. Дан однонаправленный список L1. Определить, есть ли в нем подряд идущие элементы, составляющие список L2.

  5. Дан однонаправленный список L из элементов целого типа. Из каждой группы подряд идущих равных элементов оставить в списке только один.

  6. Даны однонаправленные списки L1 и L2. Сформировать список L=L1 L2.

  7. Даны однонаправленные списки L1 и L2. Сформировать список L=L1 L2.

  8. Дан однонаправленный список L из элементов целого типа. Если в нем три подряд идущих элемента образуют строго возрастающую последовательность – исключить средний элемент.

  9. Дан однонаправленный список L. Включить в него k новых элементов, вставляя их на заданные места в списке, одновременно удаляя другие заданные элементы. Места включаемого и удаляемого элемента не должны совпадать.

  10. Дан однонаправленный список L. Пусть Li, Li+1 – два последовательных элемента списка. Получить все вещественные корни квадратного уравнения x2+Lix+Li+1=0, i=1,…,n.

  11. Дан однонаправленный список L. Найти за один просмотр списка три его максимальных элемента и переместить их в начало списка в порядке убывания.

  12. Дан однонаправленный список L. Если в нем три подряд идущих элемента составляют выпуклую тройку – исключить два крайних элемента всех таких троек.

  13. Дан однонаправленный список L из элементов целого типа. Считая, что два первых его элемента L1 и L2 соответствуют 1-му и 2-му членам арифметической прогрессии, оставить в списке только члены этой прогрессии.

  14. В однонаправленном списке L содержится n элементов (n≤12,тип элемента integer), среди которых могут быть совпадающие. Определить, сколько в списке пар одинаковых элементов, сколько троек, четверок, …, n-ок. Если в списке есть k совпадающих элементов, то при подсчете (k-1)-ок, (k-2)-ок и т.д. эти элементы не учитывать. Указание: воспользоваться алгоритмом генерации сочетаний [6] .




  1. ^ Защитное программирование


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


    1. ^ Контроль входных данных


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

Рекомендуются следующие проверки:

  1. Проверка на предполагаемый тип и диапазон измерения. Не во всех языках программирования философия строгого соответствия типов выдерживается, потому проверка совпадения типов не будет лишней. Необходимо также следить, чтобы проверка на диапазон была исчерпывающей. Например, если ожидаемое значение целочисленной переменной k может быть 1, 2 или 3, то из того, что k≠1 и k≠2 не следует, что k=3.

  2. Проверка граничных значений данных. Особое внимание необходимо уделить «нулевому варианту», когда от программы требуется «ничего не делать» – типа вычисления 0! или создания набора элементов нулевой длины.

  3. Проверка на корректность данных – реакция программы должна быть адекватной (см. раздел 1 – реакция устойчивой программы на некорректные данные).




    1. ^ Контроль промежуточных данных


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

Типичными можно считать следующие случаи некорректности промежуточных данных.

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

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

  2. Переполнение. Наиболее коварно целочисленное переполнение. В языке Pascal максимальное значение целого числа (системная константа MaxInt) равно 215 – 1=32767. Как правило ситуация целочисленного переполнения не диагностируется системой, а происходит циклическое урезание результата, то есть из ответа вычитается 65536=216. Таким образом при выполнении сложения 25000+10000 ответ будет 350000 – 65536= – 30536. Поэтому необходима проверка :

if a

Неправильно: if a + b < MaxInt then c := a+b;

При опасности вещественночисленного переполнения рекомендуется использовать нормировочные коэффициенты. Например, надо решить квадратное уравнение 1012х2+5·1018х+4·1024=0. Решение существует: х1= – 106,

х2= – 4·106, но при вычислении дискриминанта D=b2 – 4ac получим b2=25·1036 – будет переполнение. Данное уравнение можно привести к виду x2+px+q=0, поделив на а=1012.

Общая рекомендация: всегда полезно привести формулы к безразмерному виду.

  1. Потеря точности. Задача: вычислить с точностью 10– 8 значение При больших х вычислительная погрешность будет очень велика, может быть более 100%. Это происходит потому, что при больших х ряд сходится медленно, слагаемые имеют разные знаки и близки по модулю. Такая ситуация является главным источником вычислительной погрешности.

Например, 47.132–47.111=0.021, где уменьшаемое и вычитаемое известны с точностью до 5 значащих цифр, то есть их относительная погрешность . У разности известны 2 значащие цифры, то есть относительная погрешность равна . В такой ситуации рекомендуется изменить вычисляемую формулу. Например, положить . В этом варианте все слагаемые положительны, погрешности не будет.

Пример 3.1. Вычислить . Если вычислять с точностью до 8 значащих цифр, то получим U=3.54·10-5. Для получения большей точности ответа можно преобразовать формулу:

, все знаки верные.

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

^ Var p,d,e:real;

Begin readln(d); e := 1;

repeat

e := e/2;

p := d + e;

until p=d;

writeln (2*e);

End.

Здесь d – какое-либо число, относительно которого надо определить точность, например d=1. Переменная e делится на 2 потому, что деление на 2 происходит без потери точности.

  1. Переполнение динамической памяти. При выполнении некоторых алгоритмов, использующих динамическую память, получается много промежуточных результатов. Если не производить своевременную выгрузку, то очень быстро наступает переполнение динамической памяти. Выгрузку следует проводить сразу после использования промежуточного данного. Для контроля возможного переполнения в языке Pascal, например, есть специальные функции SizeOf(x) – объем памяти, требующийся под переменную х и MaxAvail – максимальный размер свободной области динамической памяти. Проверка выглядит так:

^ If SizeOf(x) < MaxAvail then new(p);

Здесь p – указатель переменной х.

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

Итак, защитное программирование является мощным средством борьбы с ошибками кодирования и проектирования. Следует, однако, помнить, что употреблять его следует в меру. Для этого надо:

  1. изучить по логике модуля все предположения о входных данных и составить список всех возможных проверок;

  2. оценить:

  • сложность каждой проверки;

  • вероятность того, что данные будут содержать ошибку;

  • последствия отсутствия проверки.

  1. выработать необходимый минимум защиты.

^ 4. Практические рекомендации по надежному программированию

4.1 О структурном программировании


        1. Еще только приступая к проектированию логики, старайтесь мыслить в терминах структурного программирования.

        2. Избегайте операторов goto всюду где возможно, но не ценой ясности программы. Оператор goto хорошо применять:

  • для аварийного выхода из цикла;

  • для перехода к концу программы;

  • во избежании слишком большой глубины вложенности ветвления.


^ 4.2 Работа с данными


              1. Явно объявляйте все переменные и все их атрибуты, не пользуйтесь умолчанием. В некоторых компиляторах выбор по умолчанию можно перенастраивать, не зная о текущей настройке, можно нарваться на неприятности.

              2. Избегайте явных констант, кроме 0 и 1.

              3. Не используйте переменную более чем для одной цели. Исключения могут составлять переменные для цикла.

              4. Не используйте особое значение переменных с особым смыслом.

Пример 4.1. Переменная ДлСтроки равна числу символов в строке А, если же ДлСтроки=0, то так сообщается об ошибке ввода, то есть особое значение 0 используется не по назначению.

              1. Будьте осторожны с двоичной машиной.

Пример 4.2. Если А=0.1 и В=0.6, то if A*B=0.06 then… может никогда не сработать, так как 0.1 и 0.6 не переводятся точно в двоичную систему.


^ 4.3 Правила хорошего тона составления

структуры программы


        1. Предусмотрите else для каждого then (if). Если ничего не надо делать по этому else, то поставьте пустой оператор, либо не используйте else вообще.

        2. Выполняйте исчерпывающие проверки.

        3. Как можно реже используйте внутренние процедуры.

        4. По возможности чаще используйте рекурсию, так как алгоритм перебора с возвратом – очень распространенное явление.

        5. Используйте свободу синтаксиса языка для придания программе лучшей читабельности:

  • физически отделяйте блоки;

  • используйте дополнительные пробелы для выделения частей операторов;

  • пишите лесенкой, не скупясь на отступы;

  • используйте большие буквы и знаки подчеркивания для придания большей выразительности именам.


4.4 Комментарии


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

              2. В начале модуля пишите его внешнюю спецификацию. Можно прокомментировать каждый законченный фрагмент и каждое ветвление.

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

              4. Физически выдвигайте комментарий из текста собственно программы.



4.5 Микроэффективность


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

  1. Игнорируйте все предложения по повышению эффективности пока программа не будет правильной. Если программа хорошо спроектирована, то ее легко модифицировать для повышения эффективности.

  2. Не жертвуйте легкостью чтения ради эффективности.

  3. Не добивайтесь эффективности ради эффективности.

  4. Добивайтесь эффективности на основе измерения, а не догадки. Доказано, что программисты очень плохо угадывают причины медленной работы программы. Только после того, как программа заработает и только если она работает не эффективно, необходимо выполнить соответствующие замеры и обнаружить пресловутые 5%, которые занимают 90% времени.




  1. Тестирование модулей

^ 5.1 Идеология тестирования


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

В отличии от тестирования, отладка – это установление точной природы обнаруженной ошибки и ее исправление, иными словами, отладка – это то, что делают после тестирования.

^ Тестирование модулей (автономное тестирование) – контроль отдельного программного модуля в изолированной среде.

Можно выделить две главные проблемы тестирования:

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

  1. Эффект «почти правильного оператора». Рассмотрим пример: необходимо проверить условие a=b=c. Предлагается следующий оператор: if (a+b+c)/3 = a then ….Для большинства данных этот оператор будет работать правильно, ошибочная реакция возникает, если только при каком-либо h будет b=a-h, c=a+h.

Единственная возможность в какой-то степени решить названные проблемы – тщательное планирование тестов.


^ 5.2 Аксиомы тестирования


  1. Невозможно тестировать собственную программу. Этому есть две причины:

  • вследствие того, что тестирование – процесс разрушительный, у автора программы обычно «не поднимается рука» на создание изощренного теста;

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

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

  1. Результат тестирования должен прогнозироваться до выполнения теста. Еще лучше пользоваться таким инструментом, который позволяет автоматически сверять ожидаемые и фактические результаты.

  2. Избегайте невоспроизводимых тестов. Часто в режиме диалога, сидя за терминалом, программист задает тестовые входные данные «с лету», чтобы просто посмотреть «что получится». Из-за такой неряшливости тесты всякий раз приходится придумывать заново.

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

  1. Готовьте тесты как для правильных, так и для неправильных данных.

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

  3. Не изменяйте программу, чтобы облегчить тестирование. Например, цикл должен выполниться 100 раз. Программист меняет его, чтобы цикл повторился только 10 раз. Может быть программист и занимается тестированием, но только другой программы.

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


^ 5.3 Методы тестирования


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

Методы тестирования откомпилированной программы можно разбить на две группы: структурное и функциональное тестирование.

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

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

Рассмотрим эти методы подробнее.

^ Структурное тестирование. Структурные тесты разрабатываются на основе анализа структуры программы, то есть используется принцип «белого ящика». Существуют следующие наборы требований, выполнение которых позволяет обеспечить определенный уровень полноты тестирования.

  1. Покрытие всех условных блоков. Согласно этому требованию набор тестов должен быть таким, чтобы каждый блок проверки некоторого условия прорабатывал дважды: когда проверяемое условие истинно и когда ложно.

  2. Покрытие всех элементарных условий. Набор тестов должен обеспечивать выполнение каждого элементарного условия в условном блоке не менее двух раз – истина и ложь.

Пример 5.1. Имеем условный оператор

if (x > 1) and (y = 0) then…

Требованию 1) удовлетворяет два теста: (х=2, у=0) и (х=2, у=5). Требованию 2) удовлетворяют четыре теста: (х=2, у=0), (х=2, у=5), (х=0, у=0), (х=0, у=5).

  1. Покрытие блоков и условий. Эти тесты должны одновременно удовлетворять требованиям 1) и 2).

^ Функциональное тестирование. Функциональные тесты составляются на основе анализа внешних функций модуля, используя принцип «черного ящика», так как структура программы не исследуется. Применяются два метода функционального тестирования.

  1. Метод эквивалентных разбиений. В области возможных значений входных данных выделяют подобласти, включающие допустимые и недопустимые значения. После этого разрабатываются тесты , покрывающие все возможные подобласти. Например, целочисленная переменная М должна принадлежать отрезку [1,12]. Следовательно, существуют три класса эквивалентности: М<1; 1≤M≤12; M>12.

  2. Метод анализа граничных значений. Составляются тесты, включающие данные, значения которых соответствуют граничным значениям классов эквивалентности и ближайшие к ним.

В соответствие с указанными методами для переменной ^ М могут быть использованы следующие тестовые значения: -5, 0, 1, 2, 6, 11, 12, 13, 20.


Контрольные вопросы и задания


        1. Для задачи решения квадратного уравнения ax2+bx+c=0 приведите пример теста, проверяющего «нулевой вариант».

        2. Имеется следующая задача. Даны три натуральных числа a, b, c, которые представляют собой длины сторон треугольника. Проверить, является ли этот треугольник равносторонним. Для данной задачи приведите пример некорректных данных (предполагается, что три вводимых числа действительно натуральные).

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



^ Варианты заданий


  1. Даны три целых неотрицательных числа a, b, c, являющихся длинами сторон треугольника. Проверить, является ли он равносторонним, равнобедренным (в этом случае надо указть длину неравной стороны) или разносторонним.

  2. Найти с максимальной точностью все вещественные и комплексные корни квадратного уравнения ax2+bx+c=0.

  3. Составить программу вычисления числа сочетаний используя для числовых данных только тип integer. Вычислить с помощью программы

  4. Даны три натуральных числа, являющихся длинами сторон треугольника. Определить при каком упорядочении они составляют арифметическую прогрессию.

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

  6. Составить программу деления двух многочленов, то есть определения коэффициентов частного и остатка.

  7. Составить программу сложения двух простых дробей, представленных парами (m,n)=m/n. Для числовых данных использовать только тип integer.



Литература





              1. Гласс Р. Руководство по надежному программированию./ Пер. с англ. М.: Мир, 1981. – 382с.

              2. Джонстон Р. Учитесь программировать. / Пер.с англ. М.: Финансы и статистика , 1989. – 368с.

              3. Информатика: учебник /Под ред. Н.В. Макаровой. – М.: Финансы и статистика, 1999. – 768с.

              4. Кнут Д. Искусство программирования для ЭВМ /Пер. с англ Т1. Основные алгоритмы. М.: Мир, 1976. – 735с. ; Т2. Получисленные алгоритмы. М.: Мир, 1977. – 724с.

              5. Липаев В.В. Проектирование программных средств. М.: Выс.шк., 1990. – 303с.

              6. Липский В. Комбинаторика для программистов /Пер. с польск. М.: Мир, 1988. – 213 с.

              7. Майерс Г. Надежность ПО. / Пер. с англ. М.: Мир, 1980. – 360с.

              8. Разработка САПР: в 10 кн. Кн.3: Федоров В.С., Гуляев Н.Б. Проектирование программного обеспечения САПР. М.: Высш. шк., 1990. – 159с.



Методические указания к практическим занятиям

по курсу «Надежность информационных систем»

для студентов дневного обучения


Составители: Бугаев Юрий Владимирович,

Полушина Ольга Львовна


Редактор Н.А. Сотникова

Корректор Н.В. Бургонова


Подписано в печать . . . Формат 60x90 1/16.

Бумага для множительных аппаратов, офсетная печать.

Усл. п.л. 1,75. Уч. - изд. л.1,5. Тираж 100 экз.

Изд. N 24. Заказ 362. Бесплатно.




Похожие:

Методические указания к практическим занятиям по курсу «Надежность информационных систем» Для студентов, обучающихся по направлению 654700 «Информационные системы» iconУкраинский государственный химико-технологический университет методические указания к практическим занятиям по дисциплине
Оборудование химических производств и предприятий строительных материалов”. Часть Алгоритм декомпозиции и синтеза технических систем...
Методические указания к практическим занятиям по курсу «Надежность информационных систем» Для студентов, обучающихся по направлению 654700 «Информационные системы» iconУкраинский государственный химико-технологический университет методические указания к практическим занятиям по дисциплине
Оборудование химических производств и предприятий строительных материалов”. Часть Алгоритм декомпозиции и синтеза технических систем...
Методические указания к практическим занятиям по курсу «Надежность информационных систем» Для студентов, обучающихся по направлению 654700 «Информационные системы» iconМетодические указания по выполнению контрольной работы Учетно-статистический факультет Кафедра автоматизированной обработки
Методические указания по дисциплинам: «Компьютерные информационные системы бухгалтерского учета»; «Компьютерные информационные технологии...
Методические указания к практическим занятиям по курсу «Надежность информационных систем» Для студентов, обучающихся по направлению 654700 «Информационные системы» iconМетодические разработки для самостоятельной подготовки студентов к практическим занятиям по хирургии
Заказан Центральным методическим кабинетом ддма как учебное пособие для студентов 4-х курсов медицинских факультетов ддма (протокол...
Методические указания к практическим занятиям по курсу «Надежность информационных систем» Для студентов, обучающихся по направлению 654700 «Информационные системы» iconФинансовый университет при правительстве российской федерации уфимский филиал Аполов Олег Геннадиевич Информационные системы предприятия Курс лекций Уфа-2012 Тема Понятие и значение информационных систем предприятия.
...
Методические указания к практическим занятиям по курсу «Надежность информационных систем» Для студентов, обучающихся по направлению 654700 «Информационные системы» iconМетодические указания к лабораторным работам для студентов специальности 060813
Методические указания предназначены для студентов, изучающих раздел «Текстовые процессоры»
Методические указания к практическим занятиям по курсу «Надежность информационных систем» Для студентов, обучающихся по направлению 654700 «Информационные системы» iconМетодические указания к семестровой работе для студентов очной
Методические указания содержат требования к содержанию, объему и оформлению раздела дипломного проекта для студентов автотранспортных...
Методические указания к практическим занятиям по курсу «Надежность информационных систем» Для студентов, обучающихся по направлению 654700 «Информационные системы» iconМетодические указания по изучению дисциплины: „ теория технических систем" для студентов IІI курса заочной формы обучения по специальности 090220 „Оборудование химических производств и предприятий строительных материалов"
Актуальной современной задачей является создание эффективных технических систем – механизмов, машин, установок, роботов и пр. Теория...
Методические указания к практическим занятиям по курсу «Надежность информационных систем» Для студентов, обучающихся по направлению 654700 «Информационные системы» iconМетодические указания и контрольное задание для студентов специальностей
Методические указания составлены применительно к программе дисциплины «Электроизоляция и перенапряжения в электрических системах»...
Методические указания к практическим занятиям по курсу «Надежность информационных систем» Для студентов, обучающихся по направлению 654700 «Информационные системы» iconМетодические указания по проведению факультативного курса «Лабораторный анализ сырья» для студентов
Методические указания предназначены преподавателям дисциплин по специальности
Разместите кнопку на своём сайте:
Документы


База данных защищена авторским правом ©lib.podelise.ru 2000-2014
При копировании материала обязательно указание активной ссылки открытой для индексации.
обратиться к администрации
Документы

Разработка сайта — Веб студия Адаманов