Каждая функция и метод в программной системе осуществляет некоторое действие. До того как подпрограмма начнет выполнять это действие, она может иметь некие виды на состояние окружающего мира, а также может констатировать состояние окружающего мира на момент завершения работы. Б. Мейер описывает эти виды и претензии следующим образом:
• Предусловия.Требования подпрограммы – то, что обязано быть истинным для того, чтобы подпрограмма могла вызываться. Если предусловия нарушены, программа не должна вызываться ни в коем случае. Ответственность за передачу качественных данных лежит на вызывающей программе (см. врезку ниже «Кто несет ответственность?»).
• Постусловия.Состояние окружающего мира на момент завершения работы подпрограммы – то, что гарантируется подпрограммой. Сам факт того, что в ней имеется постусловие, подразумевает, что подпрограмма завершит свою работу: бесконечные циклы не допускаются.
• Инварианты класса.Класс гарантирует, что данное условие всегда истинно с точки зрения вызывающей программы. Во время внутренней обработки подпрограммы инвариант может и не выполняться, но к моменту выхода из подпрограммы и передачи управления обратно к вызывающей программе инвариант обязан быть истинным. (Следует заметить, что класс не может давать неограниченное право доступа для записи к любому элементу данных, участвующему в инварианте.)
Рассмотрим контракт на создание программы, которая осуществляет вставку значения данных в упорядоченный список уникальных данных. При работе с iContract (препроцессором для языка Java, который можно загрузить с [URL 17]) этот контракт может быть реализован следующим образом:
/**г
* ©invariant forall Node n in elements() |
* n.prev() != null
* implies
* n.value().compareTo(n.prev().value()) > 0
*/
public class DBC_list {
/**
* @pre contains(aNode) == false
* @post contains(aNode) == true
*/
public void insertNode(final Node aNode) {
//…
Здесь стоит сказать, что элементы в списке всегда должны располагаться в порядке возрастания. При вставке нового элемента этот порядок уже может быть нарушен, и мы гарантируем, что элемент будет обнаружен после того, как он был вставлен в список.
Вы формулируете эти предусловия, постусловия и инварианты на целевом языке программирования, возможно, с некоторыми расширениями. Например, iContract предоставляет операторы логики предикатов – forall, exists и implies, дополняя обычные конструкции языка Java. Ваши утверждения могут сделать запрос о состоянии любого объекта, к которому имеется доступ со стороны метода, но удостоверьтесь, что запрос не окажет никакого побочного воздействия (см. ниже врезку «Утверждения и побочные условия»).
ППК и параметры-константы
Во многих случаях постусловие будет использовать параметры, переданные в метод, для проверки правильности поведения. Но если подпрограмме разрешено изменять переданный параметр, то у вас есть возможность обойти условия контракта. В отличие от языка Java, язык Eiffel не позволяет подобных действий. В данном случае для указания наших намерений, сводящихся к неизменяемости параметра в пределах метода, используется ключевое слово final (из языка Java). Это не является «защитой от дурака» – подклассы не имеют ограничений при повторном определении параметра как не являющегося окончательным. В качестве альтернативы можно использовать синтаксис variable@pre (принятый в iContract), чтобы получить исходное значение переменной, существовавшее на момент входа в метод.
Следовательно, в контракте между подпрограммой и любой потенциально вызывающей ее программой может быть записано следующее:
«Если вызывающая программа выполняет все предусловия подпрограммы, то подпрограмма гарантирует, что по завершении ее работы все постусловия и инварианты будут истинными».
Если одна из сторон нарушает условия контракта, то применяется предварительно согласованная мера, например, возбуждается исключение или происходит завершение работы программы. Что бы ни происходило, вы не ошибетесь, утверждая, что нарушение условий контракта есть ошибка. Это происходит далеко не всегда, и поэтому предусловия не должны использоваться для осуществления таких процедур, как проверка правильности данных, вводимых пользователем.
Подсказка 31: Проектируйте в соответствии с контрактами
В разделе «Ортогональность» рекомендуется создавать «скромные» программы. В данном случае упор делается на «ленивую» программу: проявите строгость в том, что вы принимаете до начала работы, и обещайте как можно меньше взамен. Следует помнить, что если в контракте указано, что вы принимаете все условия, а взамен обещаете весь мир, то вам придется написать… ну очень большую программу!
Читать дальше