Навіщо використовувати кодові контракти


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 про це мовчали!

На закінчення, без кодових контрактів, безліч фрагментів коду є такими:

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

При кодових контрактах це стає:

Аргумент заголовка може бути ненульовим рядком довжиною 0..500. Наступне ціле число - це додатне значення, яке може бути нульовим лише тоді, коли рядок порожній. Нарешті, я поверну IDefinitionоб’єкт, ніколи не нульовий.

Перевага 3: договори інтерфейсів

Третя перевага полягає в тому, що кодові договори розширюють можливості інтерфейсів. Скажімо, у вас є щось на кшталт:

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

    public void CommitChanges();

    ...
}

Як би ви, використовуючи лише твердження та винятки, гарантували, що CommitChangesможна викликати лише тоді, коли PendingChangesвін не порожній? Як би ви гарантували, що PendingChangesцього ніколи не буде null?

Перевага 4: застосування результатів методу

Нарешті, четверта користь - це можливість отримувати Contract.Ensureрезультати. Що робити, якщо при написанні методу, який повертає ціле число, я хочу бути впевненим, що значення ніколи не поступається або дорівнює нулю? У тому числі через п’ять років, після багатьох змін розробників? Як тільки метод має декілька точок повернення, Assertперетворіться на кошмар технічного обслуговування для цього.


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

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


3

Я знаю, що це питання трохи пізно, але є ще одна користь у Кодексних контрактах, про яку йдеться

Контракти перевіряються поза методом

Коли у нас є умови охорони всередині нашого методу, це місце, де відбувається перевірка і де повідомляються про помилки. Тоді нам, можливо, доведеться дослідити трасування стека, щоб з’ясувати, де власне джерело помилки.

Код контрактів розташований у межах методу, але передумови перевіряються поза методом, коли щось намагається викликати цей метод. Контракти стають частиною методу і визначає, можна його називати чи ні. Це означає, що ми отримуємо повідомлення про помилку набагато ближче до фактичного джерела проблеми.

Якщо у вас є захищений контрактом метод, який вимагається в багатьох місцях, це може бути реальною вигодою.


2

Дві речі:

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