Проектирование по контракту ( DbC ), также известное как контрактное программирование , программирование по контракту и проектирование-по-контрактному программированию , представляет собой подход к проектированию программного обеспечения .
Он предписывает разработчикам программного обеспечения определять формальные , точные и проверяемые спецификации интерфейса для компонентов программного обеспечения , которые расширяют обычное определение абстрактных типов данных с предусловиями , постусловиями и инвариантами . Эти спецификации называются «контрактами», в соответствии с концептуальной метафорой с условиями и обязательствами деловых контрактов.
Подход DbC предполагает, что все клиентские компоненты , которые вызывают операцию на серверном компоненте, будут соответствовать предварительным условиям, указанным как требуемые для этой операции.
Если такое предположение считается слишком рискованным (например, в многоканальных или распределенных вычислениях ), применяется обратный подход , то есть серверный компонент проверяет, выполняются ли все соответствующие предварительные условия (до или во время обработки запроса клиентского компонента ), и в противном случае отправляет соответствующее сообщение об ошибке.
Термин был придуман Бертраном Мейером в связи с его разработкой языка программирования Eiffel и впервые описан в различных статьях, начиная с 1986 года [1] [2] [3] и двух последующих изданиях (1988, 1997) его книги Object-Oriented Software Construction . Eiffel Software подала заявку на регистрацию товарного знака Design by Contract в декабре 2003 года, и она была получена в декабре 2004 года. [4] [5] Текущим владельцем этого товарного знака является Eiffel Software. [6] [7]
Проектирование по контракту имеет корни в работе над формальной верификацией , формальной спецификацией и логикой Хоара . Оригинальные вклады включают:
Центральная идея DbC — метафора того, как элементы программной системы взаимодействуют друг с другом на основе взаимных обязательств и выгод . Метафора пришла из деловой жизни, где «клиент» и «поставщик» договариваются о «контракте», который определяет, например, что:
Аналогично, если метод класса в объектно - ориентированном программировании предоставляет определенную функциональность, он может:
Контракт семантически эквивалентен тройке Хоара , которая формализует обязательства. Это можно обобщить с помощью "трех вопросов", на которые дизайнер должен неоднократно отвечать в контракте:
Во многих языках программирования есть средства для создания подобных утверждений . Однако DbC считает, что эти контракты настолько важны для корректности программного обеспечения , что они должны быть частью процесса проектирования. По сути, DbC рекомендует сначала писать утверждения . [ требуется цитата ] Контракты могут быть написаны с помощью комментариев кода , реализованы набором тестов или и тем, и другим, даже если для контрактов нет специальной языковой поддержки.
Понятие контракта распространяется на уровень метода/процедуры; контракт для каждого метода обычно содержит следующую информацию: [ необходима ссылка ]
Подклассы в иерархии наследования могут ослаблять предусловия (но не усиливать их) и усиливать постусловия и инварианты (но не ослаблять их). Эти правила приближают поведенческое подтипирование .
Все отношения классов находятся между классами клиента и классами поставщика. Класс клиента обязан вызывать функции поставщика, где результирующее состояние поставщика не нарушается вызовом клиента. Впоследствии поставщик обязан предоставить возвращаемое состояние и данные, которые не нарушают требования состояния клиента.
Например, буфер данных поставщика может потребовать, чтобы данные присутствовали в буфере при вызове функции удаления. Впоследствии поставщик гарантирует клиенту, что когда функция удаления завершит свою работу, элемент данных действительно будет удален из буфера. Другие контракты проектирования являются концепциями инварианта класса . Инвариант класса гарантирует (для локального класса), что состояние класса будет поддерживаться в пределах указанных допусков в конце выполнения каждой функции.
При использовании контрактов поставщик не должен пытаться проверить выполнение условий контракта (практика, известная как наступательное программирование) . Общая идея заключается в том, что код должен «жестко давать сбои», а проверка контракта должна быть подстраховкой.
Свойство DbC «fail hard» упрощает отладку поведения контракта, поскольку предполагаемое поведение каждого метода четко указано.
Этот подход существенно отличается от подхода защитного программирования , где поставщик несет ответственность за выяснение того, что делать, когда предварительное условие нарушено. Чаще всего поставщик выдает исключение, чтобы сообщить клиенту, что предварительное условие нарушено, и в обоих случаях — как DbC, так и защитное программирование — клиент должен выяснить, как на это реагировать. В таких случаях DbC облегчает работу поставщика.
Проектирование по контракту также определяет критерии корректности программного модуля:
Проектирование по контракту также может способствовать повторному использованию кода, поскольку контракт для каждого фрагмента кода полностью документирован. Контракты для модуля можно рассматривать как форму программной документации для поведения этого модуля.
Условия контракта никогда не должны нарушаться во время выполнения программы без ошибок. Поэтому контракты обычно проверяются только в режиме отладки во время разработки программного обеспечения. Позже, при выпуске, проверки контрактов отключаются для максимизации производительности.
Во многих языках программирования контракты реализуются с помощью assert . Asserts по умолчанию компилируются в режиме релиза в C/C++ и аналогичным образом деактивируются в C# [8] и Java.
Запуск интерпретатора Python с аргументом «-O» (для «оптимизации») также приведет к тому, что генератор кода Python не будет выдавать байт-код для утверждений. [9]
Это фактически устраняет затраты времени выполнения утверждений в производственном коде — независимо от количества и вычислительных затрат утверждений, используемых при разработке, — поскольку компилятор не будет включать такие инструкции в производство.
Проектирование по контракту не заменяет обычные стратегии тестирования, такие как модульное тестирование , интеграционное тестирование и системное тестирование . Скорее, оно дополняет внешнее тестирование внутренними самотестами, которые могут быть активированы как для изолированных тестов, так и в производственном коде во время фазы тестирования.
Преимущество внутренних самотестов в том, что они могут обнаружить ошибки до того, как они проявятся как недействительные результаты, наблюдаемые клиентом. Это приводит к более раннему и более конкретному обнаружению ошибок.
Использование утверждений можно рассматривать как форму тестового оракула , способ тестирования проекта путем реализации контракта.
Языки, которые изначально реализуют большинство функций DbC, включают:
Кроме того, стандартная комбинация методов в Common Lisp Object System имеет квалификаторы методов :before
, :after
которые :around
позволяют писать контракты как вспомогательные методы, среди прочего.
Различные библиотеки, препроцессоры и другие инструменты были разработаны для существующих языков программирования без собственной разработки по контрактной поддержке:
{{cite web}}
: CS1 maint: архивная копия как заголовок ( ссылка )стр. 2