Кроме того, отсутствует встроенный механизм «старых» значений; т. е. значений, которые существовали на момент входа в метод. При использовании утверждений, обеспечивающих соблюдение условий контрактов, к предусловию необходимо добавить программу, позволяющую сохранить любую информацию, которую вы намерены использовать в постусловии. Сравним это с iContract, где постусловие может просто ссылаться на «variabie@pre», или с языком Eiffel, который поддерживает принцип «old expression».
И наконец, исполняющая система и библиотеки не предназначены для поддержки контрактов, так что эти вызовы не проверяются. Это является серьезным недостатком, поскольку большинство проблем обнаруживается именно на стыке между вашей программой и библиотеками, которые она использует (более детально этот вопрос обсуждается в разделе «Мертвые программы не лгут»).
Поддержка ППК в языках программирования
Языки программирования, в которых имеется встроенная поддержка ППК (например, Eiffel и Sather[URL 12]) осуществляют автоматическую проверку предусловий и постусловий в компиляторе и исполняющей системе. В этом случае вы оказываетесь в самом выгодном положении, поскольку все базовые элементы программы (включая библиотеки)должны выполнять условия соответствующих контрактов.
Но как быть, если вы работаете с более популярными языками типа С, С++, и Java? Для этих языков существуют препроцессоры, которые обрабатывают контракты, инкапсулированные в первоначальный исходный текст как особые комментарии. Препроцессор разворачивает эти комментарии, преобразуя их в программу, которая контролирует утверждения.
Если вы работаете с языками С и С++, то попробуйте изучить Nana [URL 18]. Nana не осуществляет обработку наследования, но использует отладчик во время выполнения программы для отслеживания утверждений новаторским методом.
Для языка Java существует средство iContract [URL 17]. Оно обрабатывает комментарии (в формате JavaDoc) и генерирует новый исходный файл, содержащий логику утверждений.
Препроцессоры уступают встроенным средствам. Они довольно муторно интегрируются в проект, а другие используемые вами библиотеки останутся без контрактов. И тем не менее, они могут принести большую пользу; когда проблема обнаруживается подобным способом – в особенности та, которую по-другому найти просто невозможно, – это уже сродни работе волшебника.
ППК и аварийное завершение работы программы
ППК прекрасно сочетается с принципом аварийного завершения работы программы (см. «Мертвые программы не лгут»). Предположим, что есть метод, вычисляющий квадратные корни (подобный классу DOUBLE в языке Eiffel). Этот метод требует наличия предусловия, которое ограничивает область действия положительными числами. Предусловие в языке Eiffel объявляется с помощью ключевого слова require, а постусловие – с помощью ключевого слова ensure, так можно записать:
Sqrt: DOUBLE is
-- Подпрограмма вычисления квадратного корня
require
sqrt_arg_must_be_positive: Current >= 0;
--- ...
--- здесь происходит вычисление квадратного корня
--- ...
ensure
((Result*Result) – Current).abs <= epsilon*Current.abs;
-- Результат должен находиться в пределах погрешности
end;
Кто несет ответственность!
Кто несет ответственность за проверку предусловия, вызывающей программы или вызываемой подпрограммы? Если эта проверка реализована как часть самого языка программирования, то никто: предусловие тестируется «за кулисами» после того, как вызывающая программа обращается к подпрограмме, но до входа в саму подпрограмму. Следовательно, если необходимо явным образом проверить параметры, это должно быть выполнено вызывающей программой, потому что подпрограмма сама некогда не сможет увидеть параметры, которые нарушают ее предусловие. (В языках без встроенной поддержки вам пришлось бы окружить вызываемую подпрограмму преамбулой и/или заключением, которые проверяют эти утверждения.)
Рассмотрим программу, которая считывает с устройства ввода номер, извлекает из него квадратный корень (вызывая функцию sqrt) и выводит результат на печать. Функция sqrt имеет предусловие – ее аргумент не должен быть отрицательным числом. Если пользователь вводит отрицательное число, то именно вызывающая программа должна гарантировать, что это число не будет передано функции sqrt. Вызывающая программа может воспользоваться многими вариантами: она может завершить работу, выдать предупреждение и начать считывать другое число, она также может преобразовать число в положительное и добавить к результату, выданному функцией Sqrt, мнимую единицу. Какой бы вариант ни использовался, эта проблема определенно не связана с функцией sqrt.
Читать дальше