Опубликован: 17.10.2005 | Доступ: свободный | Студентов: 8825 / 588 | Оценка: 4.38 / 4.10 | Длительность: 41:16:00
ISBN: 978-5-7502-0255-3
Специальности: Программист
Лекция 11:

Проектирование по контракту: построение надежного ПО

Ключевые концепции

  • Утверждения - это булевы выражения, задающие семантические свойства класса и вводящие аксиомы и предусловия соответствующего абстрактного типа данных.
  • Утверждения используются в предусловиях (требования, при выполнении которых программы применимы), постусловиях (свойства, гарантируемые на выходе программ), и инвариантах класса (свойства, характеризующие экземпляры класса во время их жизни). Другими конструкциями, включающими утверждения, являются инварианты цикла и инструкции check.
  • Предусловие и постусловие программы описывают контракт между программой и ее клиентами. Контракт связывает программу, только при условии ее вызова, в состоянии, где предусловие выполняется; в этом случае программа гарантирует выполнимость постусловия на выходе. Понятие заключения контрактов между программами обеспечивает мощную метафору при построении надежного ПО.
  • Инвариант класса выражает семантические ограничения экземпляров класса. Инвариант неявно добавляется к предусловиям и постусловиям каждой экспортируемой программы класса.
  • Класс описывает одну возможную реализацию АТД; отображение класса в АТД выражается функцией абстракции, обычно частичной. Обратное отношение, обычно, не задается функцией.
  • Инвариант реализации, - часть инварианта класса - выражает корректность представления классом соответствующего АТД.
  • Цикл может иметь инвариант цикла, позволяющий вывести результат выполнения цикла, и вариант, позволяющий доказать завершаемость цикла.
  • Если класс поставляется с утверждениями, то можно формально определить, что означает корректность класса.
  • Утверждения служат четырем целям: помогают в конструировании корректных программ; помогают в создании документации, помогают в отладке, являются основой механизма исключений.
  • Язык утверждений в нашей нотации не включает логику предикатов первого порядка, но может выражать многие свойства высокого уровня благодаря вызову функций. Функции, включаемые в утверждения должны быть простыми и безупречно корректными.
  • Комбинация инвариантов и динамических псевдонимов приводит к Непрямому Эффекту Инварианта, который может стать причиной нарушения инварианта при корректности самого класса.

Библиографические замечания

Из работы Тони Хоара [Hoare 1981]:

Первым защитником использования утверждений в программировании был никто иной, как сам Алан Тьюринг. На конференции в Кембридже 24 июня 1950 г. он представил небольшой доклад "Проверка больших программ", в которой объяснял эту идею с большой ясностью. "Как можно проверить большую программу, утверждая, что она правильна? Чтобы для проверяющего задача не была слишком трудной, программист обязан написать некоторые утверждения, которые можно проверить индивидуально, и из которых корректность программы следует достаточно просто."

Понятие утверждения, представленное в этой лекции, восходит к работам по корректности программ, пионерами которых были Боб Флойд [Floyd 1967], Тони Хоар [Hoare 1969], Эдсгар Дейкстра [Dijkstra 1976], в дальнейшем описанные в [Gries 1981]. Книга "Введение в теорию языков программирования" (Introduction to the Theory of Programming Languages) [M 1990] представляет обзор этого направления.

Понятие инварианта класса пришло из Хоаровской работы [Hoare 1972a] по инвариантам типов данных. Смотри также приложения к проектированию программ в [Jones 1980], [Jones 1986]. Формальная теория морфизмов между АТД типами может быть найдена у [Goguen 1978].

Библиографические ссылки по формальным языкам спецификаций, включая Z, VDM, OBJ-2, Larch, можно найти в "Абстрактные типы данных (АТД)" . В работе [Lano 1994] , содержащей большое число ссылок, описаны ОО-формальные языки спецификаций, включая Object Z, Z++, MooZ, OOZE, SmallVDM, VDM++.

Стандарты по терминологии программных ошибок, дефектов, неисправностей опубликованы IEEE Computer Society [IEEE 1990], [IEEE1993]. Их Web-страница - http://www.computer.org

Удивительно, но немногие языки программирования поддерживают синтаксическую поддержку утверждений. Ранним примером (первым, который стал мне известен) был язык AlgolW, созданный Хоаром и Виртом [Hoare 1966], непосредственный предшественник языка Pascal. Другие включают Alphard [Shaw 1981] и Euclid [Lampson 1977], спроектированные специально для разработки корректных программ. Связь с ОО-разработкой и нотация, введенная в этой книге, навеяна утверждениями языка CLU [Liskov 1981], который никогда не был реализован. Другая, базирующаяся на CLU книга Лискова и Гуттага [Liskov 1986] является одной из немногих книг по методологии программирования, в которой глубоко обсуждаются вопросы разработки надежного ПО, предлагая подход на базе "защитного программирования", подвергнутый критике в данной лекции.

Понятие Программирования по контракту, представленное в этой лекции и разрабатываемое в оставшейся части книги, пришло из [M 1987a], продолженное в работах [M 1988], [M1989c], [M 1992a]. В работе [M 1994a] обсуждаются толерантный и требовательный подходы к проектированию предусловий, обращая особое внимание на применение этих подходов к проектированию повторно используемых библиотек, включая политику "требовательной любви". Дальнейший вклад в развитие этих идей был сделан Джеймсом Мак-Кимом [McKim 1995], [McKim 1996], [McKim 1996a], а также [Henderson-Sellers], который занимался исследованием позиции поставщика ПО.

Упражнения

У11.1 Комплексные числа

Напишите спецификацию АТД для класса COMPLEX, описывающую понятие комплексных чисел с арифметическими операциями. Исходите из точной арифметики.

У11.2 Класс и его АТД

Проверьте все предусловия и аксиомы АТД STACK, введенного в предыдущих лекциях, и покажите, отображаются ли они в классе STACK4, а если да, то как.

У11.3 Полные утверждения для стеков

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

У11.4 Экспортирование размера

Почему capacity экспортируется для реализации стеков ограниченных размеров, класс STACK2?

У11.5 Инвариант реализации

Напишите инвариант реализации для класса STACK3.

У11.6 Утверждения и экспорт

Обсудите использование функций в утверждениях, в частности, введение функции correct_index в предусловия программ put и item. Если добавить эту функцию в класс ARRAY, то какой статус экспорта следует ей дать?

У11.7 Поиск жучков (bugs)

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

У11.8 Нарушение инварианта

В этой лекции было показано, что нарушение предусловия указывает на ошибку клиента, а нарушение постусловия указывает на ошибку поставщика. Объясните, почему нарушение инварианта также указывает на ошибку поставщика.

У11.9 Генерация случайных чисел

Напишите класс, реализующий алгоритм получения псевдослучайных чисел, основанный на последовательности: ni = f(ni - 1), где функция f задана, а начальное значение n0 определяется клиентом класса. Функция не должна иметь побочных эффектов. Определение функции f можно найти в учебниках, таких как [Knuth 1981] и в библиотеках по численным методам.

У11.10 Модуль "очередь"

Напишите класс, реализующий очередь (стратегию доступа "первый пришел - первый ушел", FIFO - "first in - first out"). Задайте подходящие утверждения в стиле класса STACK этой лекции.

У11.11 Модуль "множество"

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

Постскриптум: Катастрофа Ариан 5

Когда первое издание этой книги было опубликовано, Европейское Космическое Агентство опубликовало отчет международного исследования тестирования полета космической ракеты Ариан 5, потерпевшей катастрофу 4 июня 1996 года через 40 секунд после старта, по отчету стоившего 500 миллионов долларов (незастрахованного запуска).

Причина катастрофы: ошибки в бортовой компьютерной системе. Причина этой ошибки: преобразование числа с плавающей точкой, представленного 64 битами, в 16-и битовое знаковое целое привело к выбрасыванию исключения. Число задавало горизонтальный наклон ( horizontal bias ) ракеты. Некоторые исключения в системе обрабатывались, используя механизмы языка ADA, описанные в следующей лекции. Но это исключение не обрабатывалось, поскольку ранее проведенный анализ показал, что оно не может встречаться, поэтому решено было не загромождать код обработчиком соответствующего исключения.

Реальная причина: недостаточная спецификация. Проведенный анализ был вполне корректен, - но для траектории полета ракеты Ариан 4. Программный код был повторно использован при полете ракеты Ариан 5, и предположения, хотя и оставленные в маловразумительной документации, были просто забыты. Их просто не применяли к Ариан 5. При Проектировании по контракту было бы задано предусловие:

require
horizontal_bias <= Maximum_horizontal_bias

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

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

  • Повторно используемый модуль должен быть специфицирован.
  • Язык программирования должен поддерживать механизм утверждений.
  • Спецификации являются частью самого ПО.
Александр Шалухо
Александр Шалухо
Анатолий Садков
Анатолий Садков

При заказе pdf документа с сертификатом будет отправлен только сертификат или что-то ещё?