Партнерка на США и Канаду по недвижимости, выплаты в крипто
- 30% recurring commission
- Выплаты в USDT
- Вывод каждую неделю
- Комиссия до 5 лет за каждого referral
Скрытый отказ – отказ, который не обнаруживается штатными средствами контроля состояния системы, либо обнаруживается ими спустя некоторое время после возникновения отказа. Такой отказ может послужить причиной для одного или нескольких зависимых отказов.
По связи с другими отказами
Независимый отказ – отказ, возникновение которого не обусловлено другими отказами
Зависимый отказ – отказ, возникновение которого вызвано другими отказами
Процесс верификации не гарантирует отсутствия в системе всех дефектов, которые могут вызвать сбои, отказы или аварии – речь идет только об определенном уровне отсутствия этих дефектов. Поэтому когда речь идет о системах, к надежности которых предъявляются повышенные требования, для повышения их надежности кроме верификации используются различные методы разработки устойчивого кода.
При этом в результате верификации из системы устраняются дефекты, которые могут быть выявлены при анализе требований и/или кода, а методы разработки устойчивого кода дают дополнительную гарантию того, что система сохранит работоспособность в случаях, не предусмотренных требованиями. Однако, не следует расценивать эти методы как замену верификации или грамотного проектирования.
Методы разработки устойчивого кодаКак уже было сказано в первых разделах данного курса, верификация не может гарантировать того, что в программной системе отсутствуют абсолютно все ошибки. В лучшем случае мы можем сделать заключение о том, что система ведет себя в соответствии с требованиями, работая на заданном оборудовании. Для того, чтобы нивелировать возможные негативные последствия от ошибок, не обнаруженных в процессе верификации, применяются методы разработки устойчивого программного кода или методы защитного программирования.
Защитное программирование – это метод организации программного кода таким образом, чтобы при работе системы последствия проявления дефектов в ней не приводили к сбоям, отказам и авариям. При этом защитное программирование, как правило, не дает нам никакой информации о том, где в системе находится дефект, поэтому защитное программирование нельзя рассматривать как полную замену верификации – эти два аспекта промышленной разработки систем лишь дополняют друг друга.
Основной метод защитного программирования – внедрение в программный код системы различного рода проверок на допустимость обрабатываемых системой данных или допустимость состояния системы в заданный момент времени. Таким образом, подход защитного программирования можно сформулировать таким образом: «Прежде чем делать что-то – проверь, с корректными ли данными и в корректный ли момент времени ты начинаешь это делать». Если все данные для работы корректны – система функционирует в нормальном режиме. В случае, если данные неверны, запускается специально разработанная часть системы, предназначенная для восстановления правильности функционирования и предотвращения сбоя (либо при помощи приведения данных к корректному виду, либо при помощи извещения оператора).
В настоящее время существует два основных механизма защитного программирования – проверка допущений в критических точках и обработка исключительных ситуаций.
Критические точки и допущения (assertions)В любой программе есть участки, требующие предварительных проверок перед выполнением. Например, представим себе два массива, один из которых хранит имена и фамилии людей, а второй – номера их телефонов. В нормальной ситуации количество элементов в обоих массивах должно совпадать и, при поиске фамилии в первом массиве, полученный индекс может быть использован для получения номера телефона найденного человека
string_vector surname;
string_vector phone;
…
int index = surname. find(“Петров”);
foundPhone = phone. at(index);
Однако, в случае рассинхронизации двух массивов, индекс может оказаться неверным, например, выходить за границы массива. Вообще, при использовании индекса, мы делаем неявное допущение о его корректности. В большинстве современных языков программирования (C, C++, C#, Java, Eiffel) существуют средства для явного задания таких допущений.
Например, в C существует функция assert(), определенная в заголовочном файле <assert. h>. Аргументом этой функции может выступать любое булево выражение. В случае, если оно равно false, функция прерывает работу программы. Таким образом, при помощи булевых выражений могут быть описаны допущения в критических точках программы. Предыдущий пример при использовании функции assert() будет выглядеть как
#include <assert. h>
…
string_vector surname;
string_vector phone;
…
int index = surname. find(“Петров”);
assert( (index > 0) && (index < phone. size()) );
foundPhone = phone. at(index);
Часто программисты определяют свою собственную функцию assert(), например, следующим образом:
#ifdef NODEBUG
#define assert(ignore) 0
#else
#define assert(ex) \
((ex) ? 1 : \
( printf("Assertion failed "), \
abort(), 0))
#endif // NODEBUG
Эта функция отличается от стандартной тем, что в финальной сборке системы с установленным макросом NODEBUG, выдача предупреждений функцией assert() отключается. Такая организация функции assert() связана с широко распространенным заблуждением касательно того, что частые вызовы функции assert() значительно замедляют выполнение программы. Поэтому многие программисты используют допущения только на стадии отладки, считая, что они не могут сработать в конечном продукте. Однако достаточно небольшой проигрыш в скорости окупается дополнительной гарантией надежности системы.
Другая причина того, что программисты предпочитают отключать допущения в финальной версии кода, заключается в том, что при срабатывании допущения выполнение программы прерывается. Однако, существует метод использования допущений совместно с обработкой исключений (см. следующий раздел), при котором возможно определить функции, исправляющие ошибочное состояние системы и продолжающие ее выполнение.
Существует три причины использовать допущения в критических точках:
- упрощение процесса создания корректных программ – явно задавая условия, необходимые для корректной работы программы в критических точках, мы защищаем себя от выдачи программой неверных данных; помощь в сопровождении документации и документировании – явно заданные условия позволяют проще определить, синхронизирован ли программный код с проектной документацией, которая зачастую определяет корректные и некорректные диапазоны значений обрабатываемых данных; помощь в отладке – неверные допущения проявятся в процессе отладки, в результате упростится уточнение допущений.
В зависимости от того, в какой критической точке проверяется допущение, выделяют следующие их типы:
- предусловия – такие допущения помещаются в начале функций или процессов обработки данных и предназначены для проверки того, все ли необходимые данные корректны; постусловия – такие допущения помещаются в конце функций или процессов обработки данных и предназначены для проверки полученного результата на корректность до того, как передать его дальше; инварианты классов – такие допущения предназначены для периодической проверки состояния данных объектов классов, которые не должны меняться в течение жизненного цикла объекта, или должны меняться строго определенным образом; инварианты циклов – такие допущения предназначены для проверки условий, которые должны быть всегда истинны во время выполнения цикла. Примером такого условия может служить допущение о том, что значение итератора цикла не должно превышать количества элементов массива, по которому организован цикл.
Применение допущений является основной для контрактного программирования, при котором в каждом классе явно определяются предусловия условия для использования методов этого класса. Таким образом, между объектами этого класса и другими объектами заключается своего рода «контракт» в котором четко прописан интерфейс взаимодействия не только по форматам вызова методов, но и по пересылаемым данным.
Основным источником информации для определения контрактов являются требования на систему – функциональные или архитектурные. Поэтому верификацию допущений необходимо проводить в два этапа – на первом этапе проверяется корректность условий каждого допущения по отношению к требованиям, и только затем на втором этапе моделируются ситуации, приводящие к нарушению допущения. Начинающие тестировщики часто забывают о первом этапе, переходя от функционального тестирования по требованиям к структурному тестированию по коду.
Обработка исключенийВ C++, C# и Java механизм допущений был значительно расширен. При возникновении неверных данных в критической точке вызывается не единая общесистемная функция, прерывающая выполнение программы, а пользовательская функция, которая может либо предпринять попытку разрешить проблему с данными, либо прервать выполнение программы. Возникновение проблемы в критической точке получило название исключительной ситуации или исключения, а вызываемая функция пользователя – обработчик исключения.
Рассмотрим класс, реализующий тип данных «вектор». Для получения значения элемента вектора в нем перегружается оператор [], в котором делается проверка допустимости значения индекса элемента. В качестве второго параметра функции Assert указывается метод класса, обрабатывающего исключительную ситуацию в случае, если значение индекса элемента недопустимо.
class safeVector : public vector {
public:
class outOfRangeException {
int l;
public:
outOfRangeException (const char *,
const char *, int line)
: l (line) {}
int line (void) { return l; }
};
int& safeVector::operator[] (int index) {
|
Из за большого объема этот материал размещен на нескольких страницах:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |


