Чому існує така обмежена підтримка Design by Contract на більшості сучасних мов програмування?


40

Нещодавно я відкрив Design by Contract (DbC) і вважаю надзвичайно цікавим способом написання коду. Серед іншого, здавалося б, пропонуються:

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

Витрати, порівняно, здаються досить невеликими:

  • Додаткове натискання пальцем. Оскільки договори мають бути прописані.
  • Проходить певну кількість тренувань, щоб комфортно писати контракти.

Тепер, ознайомившись насамперед з Python, я розумію, що насправді можливо записати передумови (просто викидаючи винятки за невідповідний ввід), і навіть можна використовувати твердження, щоб знову перевірити певні постусловини. Але неможливо імітувати певні функції, такі як "старий" чи "результат", без додаткової магії, яка в кінцевому підсумку вважалася б непітонічною. (Крім того, є кілька бібліотек, які пропонують підтримку, але в кінцевому підсумку я розумію, що було б неправильно використовувати їх, як це не робить більшість розробників.) Я припускаю, що це аналогічна проблема для всіх інших мов (крім звичайно , Ейфелева).

Моя інтуїція підказує мені, що відсутність підтримки має бути наслідком якогось відмови від практики, але пошук в Інтернеті не дав результатів. Мені цікаво, чи хтось може уточнити, чому, здається, більшість сучасних мов надають таку маленьку підтримку? Чи недолік DbC чи надмірно дорогий? Або це просто застаріло завдяки Екстремальному програмуванню та іншим методологіям?


Здається, занадто складний спосіб програмування, керований тестом, без переваги також тестування програми.
День

3
@Dan, не дуже, я вважаю це більше як розширення системи типів. наприклад, функція не просто бере цілий аргумент, вона бере ціле число, яке за контрактом зобов’язане бути більшим за нуль
Carson63000

4
Код контрактів @Dan значно скорочує кількість тестів, які потрібно зробити.
Рей Міясака

24
@Dan, я скоріше скажу, що TDD - це договори бідної людини, а не навпаки.
SK-логіка

Динамічною мовою ви можете "прикрасити" свої об'єкти контрактами на основі необов'язкового прапора. У мене є приклад реалізації, який використовує екологічні прапори, щоб необов'язково мавпа виправляти існуючі об'єкти контрактами. Так, підтримка не є рідною, але її легко додати. Це ж стосується тестових джгутів, вони не є рідними, але їх легко додати / записати.
Райнос

Відповіді:


9

Можливо, вони підтримуються практично в будь-якій мові програмування.

Вам потрібні "твердження".

Вони легко кодуються як "якщо" заяви:

if (!assertion) then AssertionFailure();

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

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

Ви можете зробити їх трохи ефективнішими на більшості мов, визначивши булеву постійну "перевірку" часу компіляції та трохи переглянувши висловлювання:

if (checking & !Assertion) then AssertionFailure();

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

Деякі сучасні мови надають вам приємний синтаксис, і це, на мою думку, ви маєте на увазі під «сучасною підтримкою мови». Це підтримка, але її досить тонка.

Що більшість навіть сучасних мов не дає вам "тимчасові" твердження (над довільними попередніми або наступними станами [тимчасовий оператор "врешті-решт"), які вам потрібні, якщо ви хочете написати дійсно цікаві договори. Якщо заяви не допоможуть ти тут.


Проблема, яку я маю, лише маючи доступ до тверджень, полягає в тому, що не існує ефективного способу перевірки посткондицій в командах, оскільки вам часто потрібно порівнювати стан післяумови з умовою передумови (Ейфель називає це «старим» і автоматично передає його в порядок післяумови .) У Python цю функціональність можна тривіально відтворити за допомогою декораторів, але вона виявляється короткою, коли настає час вимкнути твердження.
Цезар Баутіста

Скільки попереднього стану Ейфелева насправді економить? Оскільки він розумно не може знати, до якої частини ви можете отримати доступ / змінити, не вирішивши проблему зупинки (проаналізувавши свою функцію), вона або має зберегти повний стан машини, або, як гуерист, лише деякі дуже дрібні його частини. Підозрюю останнє; і вони можуть "моделюватися" простими скалярними завданнями перед попередньою умовою. Я б із задоволенням дізнався, що Ейфель робить інакше.
Іра Бакстер

7
... щойно перевірив, як працює Ейфелева. "old <exp>" - це значення <exp> при вході в функцію, тому він робить дрібні копії на вході в функцію, як я очікував. Ви також можете їх робити. Я погоджуся, що компілятор реалізувати синтаксис до / пост / старий - це зручніше, ніж робити все це вручну, але справа в тому, що це можна зробити вручну, і це дійсно не важко. Ми повернулися до ледачих програмістів.
Іра Бакстер

@IraBaxter Ні. Код стає простішим, якщо ви можете відокремити договір від фактичної логіки. Крім того, якщо компілятор може розрізнити контракт і код, він може зменшити дублювання на багато . Наприклад, D ви можете задекларувати контракт на інтерфейс або надклас, і твердження будуть застосовані до всіх класів, що реалізують / розширюють, незалежно від коду в їхніх функціях. Наприклад, з python або Java, ви повинні використовувати весь superметод і, можливо, викинути результати, якщо ви хочете, щоб контракти перевірялися без дублювання. Це дійсно допомагає реалізувати чистий код, сумісний з LSP.
марстато

@marstato: Я вже погодився, що підтримка мови - це гарна річ.
Іра Бакстер

15

Як ви кажете, «Дизайн за контрактом» - це особливість в Ейфелі, яка вже давно є однією з тих мов програмування, яку добре поважають у спільноті, але яка ніколи не прижилася.

DbC не є жодною з найпопулярніших мов, тому що лише порівняно недавно спільнота програмного забезпечення прийняла, що додавання обмежень / очікувань до їх коду є "розумною" річчю, яку слід очікувати програмістам. Зараз звичайно програмістам зрозуміти, наскільки цінним є тестування одиниць, і це стосується того, що програмісти більше сприймають введення коду для перевірки своїх аргументів і бачать переваги. Але десятиліття тому, напевно, більшість програмістів сказали б, що "це просто додаткова робота для речей, які ви знаєте, завжди буде добре".

Я думаю, що якби ви сьогодні пішли до середнього розробника і поговорили про пост-умови, вони захоплено кивали головою і сказали "ОК, це як тестування одиниць". І якщо ви говорите про попередні умови, вони б сказали "Гаразд, це як перевірка параметрів, що ми не завжди робимо, але, знаєте, я думаю, це нормально ..." І тоді, якщо говорити про інваріантів , вони би почали говорити: "Боже, скільки це накладних витрат? Скільки ще помилок ми збираємось зловити?" тощо.

Тому я думаю, що перед тим, як DbC буде широко прийнято, ще довгий шлях.


ОТОГ, основні програмісти досить довго використовувались для написання тверджень. Відсутність корисного препроцесора у найсучасніших основних мовах зробила цю приємну практику неефективною, але вона все ще є загальною для C та C ++. Зараз це відбувається повернення за допомогою контрактів Microsoft Code (на основі AFAIK, переписування байтового коду для версій версій).
SK-логіка

8

Моя інтуїція підказує мені, що відсутність підтримки має бути наслідком якогось відмови від практики, ...

Помилковий.

Це практика дизайну . Він може бути втілений явно в коді (Ейфелевий стиль) або неявно в коді (більшість мов) або в одиничних тестах. Практика проектування існує і працює добре. Мовна підтримка по всій карті. Однак він присутній багатьма мовами в рамках тестових рамок.

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

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

Або це просто застаріло завдяки Екстремальному програмуванню та іншим методологіям?

Немає.

Ми в основному використовуємо одиничні тести, щоб продемонструвати, що DbC виконаний.

Для Python, як ви зазначали, DbC діє в декількох місцях.

  1. Результати тесту на docstring та docstring.

  2. Твердження для перевірки входів та виходів.

  3. Одиничні тести.

Далі.

Ви можете прийняти грамотні інструменти в стилі програмування, щоб ви написали документ, що включає інформацію про DbC і який генерує чисті сценарії тестування Python плюс unit. Грамотний підхід до програмування дозволяє писати чудовий літературний фрагмент, який включає договори та повне джерело.


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

+1. Я думаю, ти єдиний, хто охопив найкритичнішу точку - there are some things which cannot be proven. Офіційна перевірка може бути чудовою, але не все перевірено! Тож ця функція фактично обмежує те, що мова програмування насправді може робити!
Діпан Мехта

@DonalFellows: Оскільки загальний випадок не може бути доведений, важко включити купу функцій, які (а) дорогі та (б), як відомо, є неповними. Моя думка у цій відповіді полягає в тому, що простіше уникати всіх цих особливостей і уникати встановлення помилкових очікувань на підтвердження формальної коректності взагалі, коли є обмеження. В якості конструкторської роботи (поза мовою) може використовуватися (і слід) безліч методів доказування.
С.Лотт

Це зовсім не дорого на такій мові, як C ++, де перевірка контрактів збирається у складанні випусків. А використання DBC, як правило, призводить до випуску коду збірки, який є більш легким, тому що ви ставите менше перевірок часу виконання програми, коли програма перебуває в законному стані. Я втратив підрахунок кількості богатих кодових баз, які я бачив, коли численні функції перевіряють наявність незаконного стану та повертають помилкові, коли НІКОЛИ не повинні знаходитись у такому стані у належно протестованій версії версії.
Кайтан

6

Просто здогадуюсь. Можливо, причиною того, що це не так популярно, є те, що "Дизайн за контрактом" є торговою маркою Ейфеля.


3

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

Іншим можливим висновком є ​​те, що популярність "керованих мов" є поточним доказом підтримки дизайну за контрактом для вибраних керованих функцій (межі масиву за контрактом тощо).


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

2

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

одна сторона цього вже була розглянута в інших відповідях, блок-тести та інші механізми виконання (або навіть деякі механізми компіляції часу з мета-програмуванням шаблонів) можуть дати вам багато користі від DbC вже. Тому, хоча є користь, вона, ймовірно, сприймається як досить скромна.

Інша сторона - це вартість, ретро вбудовуючи DbC в існуючу мову, ймовірно, занадто велика переломна зміна і дуже складна для завантаження. Ввести новий синтаксис у мові без порушення старого коду важко. Оновлення існуючої стандартної бібліотеки для використання таких далекосяжних змін було б дорогим. Тому можна зробити висновок, що реалізація функцій DbC в існуючій мові має високу вартість.

Я також зазначу, що поняття, які в значній мірі контрактують на шаблони, і тому дещо пов'язані з DbC, були виключені з останнього стандарту C ++, оскільки навіть після років роботи над ними було підрахуно, що вони ще потребують років роботи. Такі великі, широкі, мінливі зміни мов просто важко реалізувати.


2

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

Без підтримки компілятора, "DbC" - це лише антерна назва "перевірити інваріанти / припущення та викинути виняток, якщо це порушено".


Хіба це не стикається з проблемою зупинки?
Ceasar Bautista

@Ceasar Це залежить. Деякі припущення можна перевірити, інші не можуть. Наприклад, існують системи типів, які дозволяють уникнути передачі порожнього списку в якості аргументу або повернути його.
Інго

Хороший момент (+1), хоча Бертран Мейєр у своїй частині "Masterminds of програмування" згадав також про їхню систему створення випадкових класів та закликав перевірити на предмет порушення контрактів. Тож це змішаний підхід до компіляції часу / часу, але я сумніваюся, що ця методика працює у будь-якій ситуації
Maksee

Це певною мірою вірно, хоча це має бути катастрофічним невдачею, а не винятком (див. Нижче). Основна перевага DBC - це методологія, яка фактично призводить до кращо розроблених програм, і впевненість, що будь-який даний метод ОБОВ'ЯЗКОВО повинен бути в правовому стані при вступі, що спрощує значну частину внутрішньої логіки. Як правило, ви не повинні займатися викидами винятків, коли контракт порушується. Винятки слід використовувати, коли програма ЛЕГАЛЬНО перебуває у стані ~ X, і клієнтський код повинен це впоратися. У договорі буде сказано, що ~ X просто незаконне.
Кайтан

1

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

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

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

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

Твердження не такі, як повністю функціональна підтримка до / після умов та інваріантних. Хоча він може спробувати імітувати їх, але мова / компілятор з належною підтримкою робить аналіз "абстрактного синтаксичного дерева" та перевіряє на логічні помилки, які просто твердження не можуть.

Редагувати: Я здійснив пошук, і наступне обговорення пов'язане з цим, що може бути корисним: https://stackoverflow.com/questions/4065001/are-there-any-provable-real-world-languages-scala


-2

В основному причини такі:

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

1
Ваша відповідь недостатньо аргументована. Ви просто висловлюєте свою думку про те, що немає переваг, що він вимагає великої кількості коду, і що це зайве, оскільки це можна зробити з існуючими мовами (ОП спеціально вирішило це питання!).
Андрес Ф.

Я не замислююся про твердження тощо як про заміну. Це не правильний спосіб зробити це наявними мовами (тобто ще не було вирішено)
tp1

@ tp1, якби програмісти дійсно хотіли мінімізувати введення тексту, вони ніколи не потраплять у щось таке багатослівне та красномовне, як Java. І так, саме програмування вимагає "спеціалізованих знань", "щоб це зробити правильно". Тим, хто не володіє такими знаннями, просто не слід допускати кодування.
SK-логіка

@ Sk-логіка: Здається, половина світу робить OO неправильно просто тому, що вони не хочуть записувати функції переадресації з функцій-членів у функції членів даних членів. Це є великою проблемою в моєму досвіді. Це безпосередньо спричинено мінімізацією кількості символів для запису.
tp1

@ tp1, якби люди дійсно хотіли звести до мінімуму введення тексту, вони ніколи не торкалися б ОО. OOP, природно, красномовний, навіть у найкращих його реалізаціях, як Smalltalk. Я б не сказав, що це погана властивість, красномовство допомагає іноді.
SK-логіка
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.