Автор Дэйв Николетт
Несмотря на полезность Design by Contract (DbC), его поддержки крайне не хватает почти во всех основных языках программирования. Похоже, что Ruby и JavaScript - единственные основные языки, для которых доступна надежная и простая в использовании библиотека DbC.
Однако изменение собственного кода для предварительных условий и постусловий несложно на любом языке. Поддержка инвариантов - совсем другое дело, но вполне возможно, что ваше приложение может обойтись и без нее. Часто намерение инварианта может быть реализовано с помощью проверок предварительного условия и постусловия.
Это серия из трех постов на тему DbC. Часть 1 содержит:
- Объяснение дизайна по контракту
- Разъяснение назначения DbC - в частности, это не инструмент для тестирования.
- Случайные самоуверенные комментарии
Часть 2:
- Краткий обзор доступных библиотек DbC для некоторых основных языков программирования (Java, C #, Ruby, JavaScript, Python, Go)
- Небольшая демонстрация гема контрактов для Ruby
- Небольшая демонстрация договорного пакета для JavaScript
- Случайные самоуверенные комментарии
Часть 3:
- Пошаговое руководство по тестовой ручной поддержке DbC на Java
- Случайные самоуверенные комментарии
Что такое дизайн по контракту?
Разработанный Бертраном Мейером в 1980-х годах, проект «Дизайн по контракту» (DbC) представляет собой подход к разработке программного обеспечения, который фокусируется на указании контрактов, которые определяют взаимодействие между компонентами. DbC - это еще один инструмент в нашем наборе инструментов для получения уверенности в правильности нашего кода, наряду с другими инструментами, такими как системы типов, исполняемые тестовые примеры, статический анализ кода и тестирование мутаций.
В основе лежит предположение, что компоненты взаимодействуют друг с другом в модели клиент-сервер. Сервер дает определенные обещания (также называемые обязательствами) о предоставлении преимуществ клиентам, и клиентские компоненты могут предполагать, что эти обещания будут выполнены. В контракте также указаны постусловия и инварианты, которые участвующие компоненты должны соблюдать.
DbC основан на существовавших ранее логических строительных блоках. Ключевым понятием, лежащим в основе, является идея троек Хора. В 1969 году британский ученый-компьютерщик Тони Хоар предложил рассуждать о правильности программного обеспечения, рассматривая, как выполнение части кода меняет состояние вычислений. Основная формулировка:
{P} C {Q}
где P обозначает предусловие, C представляет вычисление, а Q обозначает постусловие . Предусловия и постусловия выражаются как утверждения в смысле логики предикатов (также известной как логика первого порядка). Это основные логические строительные блоки DbC.
В DbC обязательства - это предварительные условия, выраженные как утверждения; преимущества - это постусловия, выраженные как утверждения; а инварианты - это аспекты состояния системы, на которые гарантированно не повлияют вычисления.
Эйфелев язык
Было бы интересно и полезно определить способ определения контрактов как документации, которую программисты могли бы использовать в качестве руководства для создания программного обеспечения на основе DbC, но Мейер не остановился на этом. Он также разработал язык программирования, в который встроены конструкции DbC, которые применяются во время компиляции. Язык Eiffel реализует DbC напрямую.
Чтобы увидеть, как DbC может нам помочь, давайте сначала взглянем на Eiffel, поскольку это была исходная реализация. Вот образец класса Eiffel из Введение в DbC на сайте Eiffel:
class DICTIONARY [ELEMENT] feature put (x: ELEMENT; key: STRING) is -- Insert x so that it will be retrievable -- through key. require count <= capacity not key.empty ensure has (x) item (key) = x count = old count + 1 end ... Interface specifications of other features ... invariant 0 <= count count <= capacity end
Ключевое слово require определяет предварительные условия для подпрограммы put в Словаре. Одно или несколько утверждений могут следовать за require. В этом примере предварительными условиями являются то, что количество словарных статей (счетчик) не превышает емкости словаря и что клиент не пытается добавить запись без ключа.
Ключевое слово sure определяет постусловия для подпрограммы put. Он обещает, что после операции put словарь будет содержать элемент x, что поиск на основе key будет извлекать элемент x, и что новый счетчик будет на единицу больше чем предыдущий счет.
Ключевое слово invariant определяет аспекты состояния системы, которые должны оставаться неизменными. Область действия invariant больше, чем у require и sure, потому что инварианты должны выполняться независимо от того, какие операции выполняются. В этом примере инварианты состоят в том, что количество элементов в словаре (count) должно быть положительным, а количество не может превышать емкость словаря.
Не метод тестирования
В ходе поиска реализаций DbC для языков, отличных от Eiffel, я заметил распространенное заблуждение: многие люди думают о DbC как о методе тестирования. Разница между тестированием и проверкой обычно очевидна, но ситуации, в которых каждая из них может возникнуть, могут быть интерпретированы.
DbC - это форма проверки, основанная на утверждениях. Как сказали Майкл Болтон и Джеймс Бах: «Утверждение в смысле компьютерных наук - это своего рода проверка. Но не все проверки являются утверждениями, и даже в случае утверждений может быть код перед утверждением, который является частью проверки, но не частью утверждения ». DbC согласен с этим наблюдением.
Также существует путаница в отношении контекста; некоторые люди считают, что любая проверка, особенно выполняемая с помощью программного инструмента, должна происходить во время «фазы тестирования» разработки или поставки продукта. Проблемы, которые я вижу с реализациями библиотек DbC, могут быть результатом этого самого заблуждения. Многие решения DbC предназначены для использования во время разработки и тестирования. Они включают «дополнительные» и «необязательные» библиотеки, которые можно включать и отключать во время сборки или выполнения. По крайней мере, один, который я рассмотрел, требует, чтобы код был скомпилирован с включенными параметрами отладки, чтобы объектный код был инструментирован, а инструмент DbC мог использовать отладочную информацию. Это не будет сделано при подготовке кода для производственного развертывания.
Но цель состоит в том, чтобы предусловия, постусловия и инварианты утверждались в производственной среде с работающим приложением в реальном времени. В этом весь смысл. Это защита от недопустимых аргументов или неправильных структур данных, передаваемых в компоненты приложения; от компонентов, непреднамеренно генерирующих непригодные для использования или повреждающие выходы; против приложений, изменяющих состояние системы в качестве непреднамеренного побочного эффекта их операций. Каноническая эталонная реализация Eiffel не отделяет проверки DbC от остального кода. Это не цель.
Почему бы просто не написать «если»?
Когда вы смотрите на различные библиотеки DbC, вы обнаруживаете, что они, как правило, представляют собой сложные оболочки вокруг относительно простого условного кода. Одна из причин, по которой DbC не получила широкого распространения, может заключаться в том, что людей пугает кажущаяся сложность инструментов. Исследуя ценность DbC, разработчики часто спрашивают: почему бы просто не написать для этого выражение «если»?
Они правы. Проблема не в том, что нельзя просто написать «если». Проблема в том, что большинство людей не утруждают себя написанием утверждений «если». Обозначение его как отдельной «вещи» с собственным именем может незаметно побудить разработчиков обратить на него больше внимания.
Тот же Майкл Болтон, упомянутый выше, рассказывает историю о времени, когда разработчик предложил ему взломать написанное им веб-приложение. Разработчик был очень уверен, что код надежен. Болтон не только сломал приложение, но и сломал сервер за считанные секунды. Он вставил полный текст книги из Project Gutenberg в форму входа в приложение.
Итак, почему этот разработчик просто не написал если? Это избавило бы его от серьезных проблем и затруднений. Это также, возможно, фундаментальная компетенция в области разработки программного обеспечения для защиты от хорошо известных угроз безопасности. В конце концов, ограничение размера входного значения в форме - довольно простая вещь.
Вещь становится реальной, когда у нее есть имя
Возможно, если бы была вещь, которую разработчики программного обеспечения могли бы назвать и обсудить (например, Дизайн по контракту), то им было бы легче не забыть об этом позаботиться.
С ростом популярности микросервисов данное приложение может включать в себя множество небольших сервисов, которые разрабатываются независимо людьми, которые не работают вместе и никогда не встречаются. Иногда клиенты ищут службы в реестре, но даже не знают, какую реализацию интерфейса службы они вызывают. Эти приложения живут в небезопасной по своей природе среде, известной как «Интернет». Возможно, вы слышали об этом.
Некоторые из этих клиентов могут даже быть враждебными. Действительно, мы знаем, что некоторые из них такие.
DbC предлагает способ гарантировать соблюдение кодовых контрактов на практике, когда программные компоненты могут быть вызваны клиентским кодом, о котором разработчики службы ничего не знают и не контролируют. Он не защищает автоматически от всех ошибок или от решительных хакеров, но это полезный защитный инструмент.
Мы не можем полностью справиться с этой ситуацией, проверив наш код перед развертыванием. Мы должны активно управлять этим на производстве. DbC предназначен для обеспечения выполнения контракта для взаимодействия с компонентом, а не просто для его проверки.
Следующий
В следующей статье я расскажу о своем опыте поиска поддержки DbC для Java, C #, Ruby, JavaScript, Python и Go. В третьей и последней части мы рассмотрим процесс разработки поддержки DbC для нескольких основных языков.
Об авторе:
Дэйв Николетт был ИТ-специалистом с 1977 года. Он занимал различные технические и управленческие должности. Он работал главным образом консультантом с 1984 года, оставаясь одной ногой в техническом лагере, а другой в лагере менеджмента… Подробнее.
Первоначально опубликовано на www.leadingagile.com 7 мая 2018 г.