Code contracts vs валідація вхідних даних

Правила валідації вхідних даних часто приймають за контракти в коді (як, власне, і навпаки). Давайте розберемося в чому відмінність між цими двома поняттями і в яких випадках вони застосовуються.

Code contracts: передумови
Яку мету переслідує використання контрактів в коді, а саме передумов, марнослівями і інваріанти? Ідея тісно пов'язана з Fail-fast принципом: чим швидше ви помітите несподіване поведінку, тим швидше ви його виправите.

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

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

Це основна відмінність, усі інші є наслідком з нього. Давайте подивимося на них докладніше.

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

image

Червоні сигнали позначають невірні взаємодії (або невалідні дані), що йдуть від користувачів або інших додатків. Зелені сигнали означають допустимі взаємодії. Ваша мета як розробника — переконатися, що невалідні динні не можуть проникнути всередину системи.

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

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

image

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

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

У самому справі, якщо користувач вводить «десять» в ціле поле, ви не хочете, щоб ваш додаток обрушилося. Замість цього, ви ввічливо вказуєте користувачеві на помилку.

Іншої сторони, передумови контрактів роблять припущення про те, що дані всередині вашої системи знаходяться в валідному стані. Якщо це не так, ваш додаток закралася помилка.

Передумови контрактів: best practices
Давайте подивимося, що може розглядатися як передумова контракту. Контракт — це публічна оферта, запропонована класом-сервісом. У ній говориться, що якщо клієнт слід деяким правилам (предусловиям), то сервіс гарантує деякі результати, описані в постусловиях.

Це призводить до наступним характеристикам, яким має слідувати кожне передумова контракту:

  • Передумови повинні бути публічними. Це означає, що розробник класу-клієнта повинен мати можливість дізнатися про них перед тим як почне писати код.
  • Вони повинні бути легко перевіряються. Розробник клієнта не повинен писати складних алгоритмів для їх перевірки перед викликом методу.
  • Передумови не повинні покладатися на непублічне стан, т. к. це обмежує можливості клієнта за їх попередньої перевірки.
  • Вони повинні бути стабільними. Це означає, що результат виконання перевірки передумови не повинен залежати від зовнішнього стану.
Давайте розглянемо ці елементи більш докладно. Перший досить простий, він означає, що всі передумови повинні бути певним чином описані, так що розробник може ознайомитися з ними перед використанням класу. C# поки що не дозволяє вбудовувати контракти прямо в сигнатуру методів, так що найкращим на поточний момент способом є опис їх на початку методу.

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

public int Distribute(int amount)
{
Contract.Requires(CanDistribute(amount));
return DistributeCore(amount);
}

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

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

public string ReadFile(string filePath)
{
Contract.Requires(File.Exists(filePath));
// Rest of the method
}

У цьому прикладі файл може бути видалений або стати недоступним в проміжку між викликом методу ReadFile і початком перевірки контракту, і клієнт не може нічого зробити для того, щоб уникнути цього. Така перевірка не є передумовою контакту, оскільки вона не говорить про наявність бага в клієнтському коді, тому ми не можемо вносити її як передумова.

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

Посилання на оригінал статті: C# code contracts vs input validation

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.