Зачем мне использовать контракты кода


26

Недавно я наткнулся на структуру Microsoft для контрактов кода.

Я прочитал немного документации и обнаружил, что постоянно спрашиваю: «Зачем мне это когда-либо хотеть, потому что он не делает и часто не может выполнить статический анализ».

Теперь у меня уже есть своего рода защитный стиль программирования с такими исключениями:

if(var == null) { throw new NullArgumentException(); }

Я также часто использую NullObject Pattern и редко сталкиваюсь с какими-либо проблемами. Добавьте модульные тесты, и все готово.

Я никогда не использовал утверждения и никогда не пропускал их. Наоборот. Я действительно ненавижу код, в котором много бессмысленных утверждений, который просто шумит для меня и отвлекает меня от того, что я действительно хочу увидеть. Контракты с кодами, по крайней мере, Microsoft так же, и даже хуже. Они добавляют много шума и сложности в код. В 99% случаев, в любом случае, будет выдано исключение - поэтому мне все равно, от утверждения или контракта или от реальной проблемы. Просто очень, очень мало случаев, когда состояния программы действительно искажаются.

Так что, честно говоря, в чем выгода использования кодовых контрактов? Есть ли вообще? Если вы уже используете модульные тесты и код для защиты, я чувствую, что введение контрактов просто не стоит затрат и создает шум в вашем коде, который ругается сопровождающему, когда он обновляет этот метод, так же, как я делаю, когда не вижу, что делает код из-за бесполезных утверждений. Я до сих пор не вижу веской причины платить эту цену.



1
Почему вы говорите: «Статический анализ не выполняется и часто не может быть выполнен»? Я думал, что это было одним из пунктов этого проекта (в отличие от простого использования подтверждений или броска защитного исключения)?
Carson63000

@ Carson63000 Внимательно прочитайте документацию, и вы обнаружите, что статическая проверка просто выдаст много-много предупреждений, наиболее ненужных (разработчики признают, что это так), и что большинство определенных контрактов просто выбросят исключение и ничего не сделают, кроме ,
Сокол

@Falcon: как ни странно, мой опыт совершенно другой. Статический анализ работает не безупречно, но довольно хорошо, и я редко вижу ненужные предупреждения, даже когда работаю на уровне предупреждений 4 (самый высокий).
Арсений Мурзенко

Я думаю, мне придется попробовать это, чтобы выяснить, как это ведет себя.
Сокол

Ответы:


42

Вы могли бы также спросить, когда статическая типизация лучше, чем динамическая. Дебаты, которые бушуют годами, конца не видно. Итак, взгляните на такие вопросы, как изучение языков динамически и статически типизированных

Но основной аргумент - это возможность обнаруживать и исправлять проблемы во время компиляции, которые в противном случае могли бы попасть в производство.

Контракты против Охранников

Даже с охраной и исключениями вы все еще сталкиваетесь с проблемой, что система в некоторых случаях не сможет выполнить свою намеченную задачу. Возможно, это будет критический и дорогостоящий случай.

Контракты против модульных тестов

Обычный аргумент в пользу этого - тесты подтверждают наличие ошибок, а типы (контракты) - отсутствие. F.ex. используя тип, который вы знаете, что ни один путь в программе не может предоставить неверный ввод, в то время как тест может только сказать вам, что пройденный путь предоставил правильный ввод.

Образцы контрактов против нулевого объекта

Теперь это, по крайней мере, в том же парке. Такие языки, как Scala и Haskell, добились большого успеха благодаря этому подходу к полному исключению пустых ссылок из программ. (Даже если Scala формально разрешает нулевые значения, соглашение никогда не использует их)

Если вы уже используете этот шаблон для устранения NRE, вы, по сути, удалили самый большой источник сбоев во время выполнения, в основном, как это позволяют контракты.

Разница может заключаться в том, что в контрактах есть возможность автоматически запрашивать весь ваш код, чтобы избежать нуля, и, таким образом, заставлять вас использовать этот шаблон в большем количестве мест для прохождения компиляции.

Кроме того, эти контракты также дают вам гибкость в достижении целей, выходящих за рамки нуля. Поэтому, если вы больше не видите NRE в своих ошибках, вы можете использовать контракты, чтобы задушить следующую наиболее распространенную проблему, которая может у вас возникнуть. Выкл по одному? Индекс вне диапазона?

Но...

Все это, как говорится. Я согласен с тем, что контракты по синтаксическому шуму (и даже структурному шуму) добавляют к коду довольно существенный результат, и влияние анализа на время сборки не следует недооценивать. Поэтому, если вы решите добавить контракты в свою систему, вероятно, было бы целесообразно сделать это очень осторожно, с узким фокусом на том, к какому классу ошибок можно обратиться.


6
Это отличный первый ответ для программистов. Рад иметь ваше участие в сообществе.

13

Я не знаю, откуда исходит утверждение, что «он не выполняет и часто не может выполнять статический анализ». Первая часть утверждения явно неверна. Второй зависит от того, что вы подразумеваете под «часто». Я бы предпочел сказать, что часто он выполняет статический анализ и редко терпит неудачу. В обычных бизнес-приложениях редко становится намного ближе к никогда .

Итак, первое преимущество:

Преимущество 1: статический анализ

Обычные утверждения и проверка аргументов имеют недостаток: они откладываются до выполнения кода. С другой стороны, контракты кода проявляются на более раннем уровне, либо на этапе кодирования, либо при компиляции приложения. Чем раньше вы поймаете ошибку, тем дешевле ее исправить.

Выгода 2: вроде всегда актуальная документация

Контракты на кодекс также предоставляют своего рода документацию, которая всегда актуальна. Если XML-комментарий метода SetProductPrice(int newPrice)говорит, что он newPriceдолжен быть больше или равен нулю, вы можете надеяться, что документация обновлена, но вы также можете обнаружить, что кто-то изменил метод так, чтобы он newPrice = 0вызывал ArgumentOutOfRangeException, но никогда не изменял соответствующую документацию. Учитывая корреляцию между контрактами кода и самим кодом, у вас нет проблемы с несинхронизированной документацией.

Документация, предоставляемая контрактами на код, также полезна тем, что часто XML-комментарии плохо объясняют приемлемые значения. Сколько раз я был интересно , если nullили string.Emptyили \r\nявляется авторизированным значение для метода, а также комментарии XML ничего не говорится о том , что!

В заключение, без контрактов кода, много частей кода таковы:

Я приму некоторые значения, но не другие, но вам придется угадывать или читать документацию, если таковые имеются. На самом деле, не читайте документацию: она устарела. Просто переберите все значения, и вы увидите те, которые заставляют меня выдавать исключения. Вы также должны угадать диапазон значений, которые могут быть возвращены, потому что даже если бы я рассказал вам немного больше о них, это может быть неверно, учитывая сотни изменений, внесенных в меня за последние годы.

С контрактами кода это становится:

Аргумент title может быть ненулевой строкой длиной 0..500. Целое число, которое следует, является положительным значением, которое может быть нулевым, только если строка пуста. Наконец, я верну IDefinitionобъект, никогда ноль.

Преимущество 3: контракты интерфейсов

Третье преимущество заключается в том, что контракты кода расширяют возможности интерфейсов. Допустим, у вас есть что-то вроде:

public interface ICommittable
{
    public ICollection<AtomicChange> PendingChanges { get; }

    public void CommitChanges();

    ...
}

Как бы вы, используя только утверждения и исключения, гарантировали, что CommitChangesможно вызывать только тогда, когда PendingChangesне пусто? Как бы вы гарантировали, что PendingChangesникогда не будет null?

Преимущество 4: применение результатов метода

Наконец, четвертое преимущество заключается в возможности получить Contract.Ensureрезультаты. Что если при написании метода, который возвращает целое число, я хочу быть уверенным, что значение никогда не будет меньше или равно нулю? В том числе пять лет спустя, после того, как многие из разработчиков пострадали от изменений? Как только метод имеет несколько точек возврата, это Assertстановится кошмаром обслуживания.


Считайте кодовые контракты не только средством корректности вашего кода, но и более строгим способом написания кода. Аналогичным образом, человек, который использовал исключительно динамические языки, может спросить, зачем вам применять типы на уровне языка, тогда как вы можете делать то же самое в утверждениях, когда это необходимо. Вы можете, но статическая типизация проще в использовании, менее подвержена ошибкам по сравнению с кучей утверждений и самодокументированием.

Разница между динамической типизацией и статической типизацией чрезвычайно близка к разнице между обычным программированием и программированием по контрактам.


3

Я знаю, что это немного поздно для вопроса, но есть еще одна выгода для Code Contracts, о которой стоит упомянуть

Контракты проверяются вне метода

Когда у нас есть защитные условия внутри нашего метода, это место, где происходит проверка и где сообщается об ошибках. Тогда нам, возможно, придется исследовать трассировку стека, чтобы выяснить, где в действительности находится ошибка.

Контракты кода находятся внутри метода, но предварительные условия проверяются вне метода, когда что-либо пытается вызвать этот метод. Контракты становятся частью метода и определяют, может ли он быть вызван или нет. Это означает, что мы получаем сообщение об ошибке гораздо ближе к фактическому источнику проблемы.

Если у вас есть защищенный контрактом метод, который вызывается во многих местах, это может быть реальным преимуществом.


2

На самом деле две вещи:

  1. Благодаря поддержке инструментов, VS может отделить данные контракта от различий, чтобы они были частью автозаполнения.
  2. Когда подкласс переопределяет метод, вы теряете все свои чеки (которые все равно должны быть действительными, если вы следуете LSP) с контрактами, которые они автоматически следуют за своими дочерними классами.
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.