Проектирование по контракту (DbC), также известное как программирование по контракту и программирование по контракту, является подходом к разработке компьютерного программного обеспечения.Он предписывает, чтобы разработчики программного обеспечения определяли формальные, точные и проверяемые спецификации интерфейса для компонентов программного обеспечения, которые расширяют обычное определение абстрактных типов данных предусловиями, постусловиями и инвариантами.Эти спецификации называются «контрактами» в соответствии с концептуальной метафорой с условиями и обязательствами деловых контрактов. Википедия
Ярлык.
Если вы следуете хорошей практике кодирования для интерфейсов, вызнать, что интерфейс определяет контракт, которому должны соответствовать все классы реализации.
Мы разработали контрактную Java, расширение Java, в котором контракты методов указываются в интерфейсах.Мы определили три цели проектирования.
- Во-первых, контрактные Java-программы без контрактов и программы с полностью выполненными контрактами должны вести себя так, как если бы они выполнялись без контрактов в Java.
- Во-вторых, программыскомпилированный с помощью обычного компилятора Java, должен иметь возможность взаимодействовать с программами, скомпилированными по Java-контракту.
- Наконец, если класс не заявляет, что он соответствует определенному контракту, его никогда не следует обвинять в невыполнении этого контракта.Абстрактно, если вызывается метод m объекта с типом t, вызывающий объект должен быть обвинен только для контрактов предварительного условия, связанных с t, а m должен быть обвинен только для контрактов постусловия, связанных с t.Эти цели разработки поднимают несколько интересных вопросов и требуют решений, которые бы уравновешивали дизайн языка с проблемами разработки программного обеспечения.В этом разделе описываются все основные проблемы проектирования, альтернативы, наши решения, наши обоснования и последствия решений.Решения не являются ортогональными;некоторые из более поздних решений зависят от более ранних.
Контракты в Java Контракта являются украшениями сигнатур методов в интерфейсах.Каждое объявление метода может содержать выражение перед условием и выражение после условия;оба выражения должны быть булевыми.Предварительное условие указывает, что должно быть истинно при вызове метода.Если это не удается, виноват контекст вызова метода за то, что он не использует метод в надлежащем контексте.Выражение после условия указывает, что должно быть истинно при возврате метода.В случае неудачи виноват сам метод, который не установил обещанные условия.Контракт Java не ограничивает выражения контракта.Тем не менее, хорошая дисциплина программирования диктует, что выражения не должны способствовать результату программы.В частности, выражения не должны иметь побочных эффектов.Выражения как до, так и после условия параметризуются по аргументам метода и псевдопеременным this.Последний привязан к текущему объекту.Кроме того, постусловие контракта может ссылаться на имя метода, которое связано с результатом вызова метода.Контракты применяются в зависимости от типа контекста вызова метода.Если тип объекта является типом интерфейса, вызов метода должен соответствовать всем контрактам в интерфейсе.Например, если объект реализует интерфейс I, вызов одного из методов I должен проверить это предварительное условие и постусловие, указанное в I. Если тип объекта является типом класса, у объекта нет договорных обязательств.Поскольку программист всегда может создать интерфейс для любого класса, мы оставляем объекты с типами классов непроверенными по соображениям эффективности.Для примера рассмотрим интерфейс RootFloat:
interface RootFloat {
float getValue ();
float sqRoot ();
@pre { this.getValue() >= 0f }
@post { Math.abs(sqRoot * sqRoot - this.getValue()) < 0.01f }
}
Он описывает интерфейс для класса-оболочки float, который предоставляет метод sqRoot.Первый метод, getValue, не имееткаратов.Он не принимает аргументов и возвращает развернутый float.Метод sqRoot также не принимает аргументов, но имеет контракт.Предварительное условие утверждает, что развернутое значение больше или равно нулю.Тип результата sqRoot это float.Постусловие гласит, что квадрат результата должен быть в пределах 0,01 от значения с плавающей запятой.Несмотря на то, что язык контракта достаточно силен, чтобы в некоторых случаях указывать полное поведение, например, в предыдущем примере, полная или даже частичная корректность не является нашей целью при разработке этих контрактов.Как правило, контракты не могут выразить полное поведение метода.На самом деле существует разница между количеством информации, отображаемой в интерфейсе, и количеством проверок, которые могут удовлетворить контракты.В качестве примера рассмотрим следующий интерфейс стека:
interface Stack {
void push (int i);
int pop ();
}
Поскольку в интерфейсе доступны только операции push и pop, невозможно указать, что после push верхний элемент в стеке является элементом, которыйбыл просто нажал.Но если мы добавим интерфейс с помощью операции top, которая обнаружит самый верхний элемент в стеке (не удаляя его), мы можем указать, что push добавляет элементы в верхнюю часть стека:
interface Stack {
void push (int x);
@post { x = this.top() }
int pop ();
int top ();
}
InРезюме, мы не ограничиваем язык контрактов.Это делает контрактный язык максимально гибким;оценка выражения контракта может даже способствовать окончательному результату вычисления.Несмотря на гибкость языка контрактов, не все желаемые контракты являются выразимыми.Некоторые контракты невыразимы, потому что они могут включать проверку неразрешимых свойств, в то время как другие невыразимы, потому что интерфейс не позволяет достаточно наблюдений.