Контракт
Контракт - правило, обеспечивающее соблюдение программой формальных требований. Является центральным понятием контрактного программирования.
Основная идея контрактного программирования - заменить неформальные соглашения в коде формальными. Например, в языках без поддержки контрактов автор некой функции может указать в документации условия для входных данных этой функции - такой подход не дает гарантии, что пользователь прочитает документацию и будет использовать функцию правильно. И наоборот - программист может давать неформальные гарантии о поведении функции или о ее результатах, но де-факто нарушать их вследствие ошибки. Контрактное программирование позволяет уменьшить риск подобных недоразумений путем введения формальных спецификаций для компонентов программы непосредственно в коде.
Использование контрактов позволяет сделать программу более надежной без необходимости постоянного регрессионного тестирования - иными словами, избежать ошибок, когда после внесения изменений в программу перестает работать то, что работало ранее. В этом смысле контрактное программирование является альтернативой разработке через тестирование (test-driven development), но ничто не мешает использовать обе парадигмы одновременно - в некоторых языках (например, в D) они дополняют друг друга. Однако некоторые виды контрактов позволяют верифицировать то, что невозможно верифицировать при помощи тестов.
В основе парадигмы контрактного программирования лежит логика Хоара - способ формального доказательства корректности алгоритма. Она оперирует такими понятиями, как предусловие, постусловие и инвариант.
Виды контрактов
- Предусловие (precondition) - контракт, который должен быть исполнен перед выполнением функции. Применяется к параметрам функции
- Постусловие (postcondition) - контракт, который должен быть исполнен после завершения функции. Применяется к возвращаемому значению функции
- Инвариант (invariant) - контракт, который должен быть исполнен после изменения некоего состояния. Используется для проверки соответствия состояния условию.
К контрактам также можно отнести следующие языковые конструкции:
- Утверждение корректности (assertion) - условие, которое должно быть выполнено до перехода к следующему утверждению в потоке
- Интерфейс (interface) - контракт, гарантирующий доступ к определенным методам класса
- Атрибуты (attributes) - правила семантического анализатора, гарантирующие соблюдение определенных условий на этапе компиляции. Например, в языке D есть следующие атрибуты:
- pure - чистая функция. Такие функции не имеют доступа к внешнему контексту и внешним функциям и могут вызывать только другие чистые функции, что гарантирует отсутствие побочных эффектов. Возвращаемое значение чистой функции зависит только от ее параметров
- nothrow - функция, которая не выбрасывает исключение
- @safe - безопасная функция. Безопасные функции обязаны соблюдать ряд требований: например, в них не разрешается приведение и арифметика указателей, использование системных вызовов, приведение неизменяемого типа к изменяемому и др.
- @trusted - доверенная функция. Является функцией, которая может быть вызвана из безопасной функции, но безопасность которой не проверяется компилятором. Атрибут служит своеобразным компромиссом между формальным и неформальным контрактом
- @nogc - функция, не использующая сборщик мусора. В таких функциях запрещены некоторые операции языка, связанные с динамическим выделением памяти
- const - атрибут-спецификатор типа, обеспечивающий доступ к данным только для чтения
- immutable - атрибут-спецификатор типа, обеспечивающий неизменяемость данных.