AlgStr / Библиотека / ЛЕКЦИИ / PZ00 / Набок Ирина
.DOCИсключительные ситуации
Устойчивая программа — это такая программа, которая ведет себя корректно даже в случае ошибки.
В случае ошибки программа может быть неспособна вести себя точно так же, как если бы не было ошибки, но она должна вести себя определенным, предсказуемым образом. В идеальном случае она должна продолжать работать после ошибки, осуществляя некоторую аппроксимацию ее поведения при отсутствии ошибки. Говорят, что такая программа обеспечивает хорошую деградацию. В худшем случае она должна остановиться с сообщением об ошибке, не испортив при этом никаких постоянных данных.
Принцип защитного программирования состоит в том, чтобы составлять каждую программу так, чтобы она защищала саму себя от ошибок. Защитное программирование облегчает отладку программ. При эксплуатации оно еще более ценно, так как исключает возможность того, что незначительная ошибка приведет к серьезной проблеме, как, например, к порче базы данных.
Один из способов оповещения обратившегося — это определить специфический результат, возвращаемый в случае ошибки. Например, процедура факторизации может возвратить нуль, если ее аргумент не положительное число. Однако это не очень хорошее решение. Использующий данную процедуру программист может проигнорировать эту ошибку по невнимательности. Кроме того, если каждое значение возвращаемого типа есть возможный результат процедуры, то это решение не приводит к положительному результату, так как нет значения, с помощью которого можно было бы оповестить об ошибке. Вместо того чтобы иметь одну область определения, мы разделим область определения D на некоторое число подмножеств, объединение которых дает D:
D=DoU...UDn
Каждое подмножество «особо» в том смысле, что процедура ведет себя по-разному на каждом из них. Мы обеспечим оповещение, имея различные области изменения для каждого подмножества области определения и придав различным случаям различные имена. Произвольно дадим случаю аргумента из Do имя «обычный». Имена для других случаев могут быть выбраны тем, кто определяет процедуру. Таким образом, имеем: do —>- обычный (ro) di ->- имя1(ri) ,Dn ->имя n(Rn)где имя1, ..., имяn — имена «исключительных» ситуаций, соответствующих аргументам, принадлежащим Di,... Dn соответственно. Имя1 ..., имяn называются исключительными ситуациями.
При реализации процедуры программист должен гарантировать, что она во всех ситуациях закончится в соответствии со спецификацией. Следует сигнализировать только об исключительных ситуациях, указанных в спецификации. Сигнализировать следует, кроме того, только по правильным причинам. При создании реализации разумно использовать исключительные ситуации вызываемых процедур и в зависимости от обстоятельств распространять или маскировать их.
SIGNALS: список имен и результатов исключительных ситуаций
Это предложение — часть заголовка. Если исключительных ситуаций не имеется, оно может быть опущено.
Секция effects должна определять, что приводит к вызову каждой исключительной ситуации и что делает процедура в каждом таком случае.
Язык программирования может сделать работу о исключительными ситуациями легкой, если он включает в себя механизм исключительных ситуаций. Этот механизм должен обеспечивать для процедуры возможность сигнализировать об исключительных ситуациях, т. е. доводить их возникновение до сведения использующего процедуру. Кроме того, эту информацию должна быть способна получать вызывающая процедура, чтобы обрабатывать исключительные ситуации. Однако вызывающая процедура не должна обрабатывать исключительные ситуации, которые не могут возникнуть.
Исключительные ситуации следует использовать для устранения большинства ограничений, перечисленных в предложениях requires. Эти предложения следует оставлять только из соображений эффективности или если контекст использования настолько ограничен, что мы можем быть уверены, что ограничения удовлетворяются.
Исключительные ситуации являются механизмом взаимодействия программ, а не программ с пользователями. Для взаимодействия с пользователями на какое-нибудь устройство вывода может быть выдано сообщение об ошибке. Заметим, что реакция на ошибку определяется в спецификации абстракции.
Однако исключительные ситуации не всегда связаны с ошибками. Для некоторых абстракций может быть более чем один тип обычного поведения процедур, и в этом случае исключительные ситуации — удобный инструмент. Они предоставляют средства для обеспечения нескольких типов поведения и дают возможность вызывающему процедуру различать между различными случаями.
Исключительные ситуации в языке Паскаль
Для обработки сбоев в программах на языке Паскаль мы задаем некоторую процедуру, называемую failure. Эта процедура помещается в начало программы, и к ней может обращаться любая другая операция в этой программе. Спецификация данной процедуры следующая:
procedure failure (s: error.msg)
modifies Выходной поток на основном устройстве вывода.
effects Печатает следующую строку на основном устройстве вывода: "Сбой. Программа окончилась из-за:" + s, а затем выполнение программы останавливается.
Аргумент при обращении должен, конечно, идентифицировать проблему и операцию, в которой она произошла. Обычно тип error.msg будет объявляться как некоторый тип строк. Если в используемом языке Паскаль строки не поддерживаются, аргумент может быть представлен как упакованный массив символов.
function searchi (var a: int. array; х: integer; var i: integer):searchi _exceptions
modifies i
effects Если переменная х находится в массиве а, то индекс i устанавливается таким, что а [i] = х, и возвращается значение ok; в противном случае возвращается значение not-in.
где searchi-exceptions является вычисляемым типом type searchi_exceptions == (ok, not-in)
Отметим, что функция searchi имеет третий аргумент — параметр i с типом var. Эта переменная используется для получения результата в нормальном случае.
По соглашению все обращения к этой функции мы вставляем в предложение case следующим образом:
case searchi (а, х, i) of
ok: a[i]:=a[i]+l;
not-in: write («Элемент не найден»)
end
Предложение case должно иметь ветвь, соответствующую каждому значению, которое может быть возвращено функцией. (Если возможны только две альтернативы, как в случае с функцией searchi, результат мог бы иметь булевский тип, а не вычисляемый тип, и оператор if мог бы использоваться вместо предложения case.)
Этот подход имеет несколько недостатков. Неудобно, что функция searchi модифицирует параметр с типом var вместо выдачи индекса, поскольку мы не сможем тогда ее использовать в таких выражениях, как a [search I (a, x)] Более того, функция searchi не так надежна при использовании, как процедура search. Например, предположим, что мы решили добавить еще одно значение в тип searchi_exceptions. В соответствии с нашими соглашениями надо добавить еще одну ветвь к каждому предложению case, в котором имеется обращение к функции searchi. Если, однако, это не будет сделано, то не возникнет ни ошибки во время компиляции, ни ошибок во время счета. (В языке Паскаль, если переключатель в предложении case не соответствует никакой из ветвей, программа продолжает выполнение g предложения, следующего за предложением case.)