але збій програмного забезпечення вашого клієнта все ще не є хорошою справою
Це, звичайно, хороша річ.
Ви хочете, щоб все, що залишає систему у невизначеному стані, зупинило її систему, оскільки невизначена система може робити неприємні речі, такі як пошкоджені дані, форматувати жорсткий диск та надсилати президенту загрозливі листи. Якщо ви не можете відновити і повернути систему у визначений стан, відповідальність за це має бути збоєм. Саме тому ми будуємо системи, які виходять з ладу, а не тихо розриваються. Тепер впевнені, що всі ми хочемо стабільної системи, яка ніколи не виходить з ладу, але ми хочемо це робити лише тоді, коли система перебуває у визначеному передбачуваному безпечному стані.
Я чув, що винятки як контрольний потік вважаються серйозним антипаттером
Це абсолютно правда, але це часто неправильно зрозуміло. Коли вони винайшли систему виключень, вони побоювалися, що вони порушують структуроване програмування. Структурне програмування тому у нас є for
, while
, until
, break
, і , continue
коли все , що потрібно, щоб зробити все , що є goto
.
Діжкстра навчив нас, що неформальне використання goto (тобто стрибки навколо, куди вам подобається) робить читання коду кошмаром. Коли вони давали нам систему винятків, вони боялися, що вони винаходять гото. Тож вони сказали нам не «використовувати його для контролю потоку», сподіваючись, що ми зрозуміємо. На жаль, багато хто з нас цього не зробили.
Як не дивно, ми часто не зловживаємо винятками для створення коду спагетті, як ми звикли з goto. Сама порада, схоже, викликала більше клопотів.
Принципово винятки стосуються відкидання припущення. Коли ви просите зберегти файл, ви припускаєте, що файл може бути і буде збережений. Виняток, який ви отримуєте, коли це не може бути причиною того, що ім’я є незаконним, HD є повним або через те, що щур прогризав ваш кабель даних. Ви можете обробляти всі ці помилки по-різному, ви можете їх обробляти однаково або ви можете дозволити їм зупинити систему. У вашому коді є щасливий шлях, де ваші припущення мають бути справдими. Так чи інакше винятки знімають вас із того щасливого шляху. Строго кажучи, так, це свого роду "контроль потоку", але це не те, про що вони вас попереджали. Вони говорили про дурниці так :
"Винятки повинні бути винятковими". Ця маленька тавтологія народилася тому, що дизайнерам систем винятків потрібен час для створення слідів стека. У порівнянні зі стрибками навколо, це повільно. Він їсть час процесора. Але якщо ви збираєтеся увійти та зупинити систему або принаймні зупинити поточну обробку часу, перш ніж запустити наступну, то у вас є деякий час, щоб вбити. Якщо люди починають використовувати винятки "для контролю потоку", ці припущення про час усі виходять із вікна. Тож "Винятки повинні бути винятковими" нас справді розглядали як розгляд ефективності.
Набагато важливіше, ніж це, не бентежить нас. Скільки часу вам знадобилося помітити нескінченний цикл у наведеному вище коді?
НЕ повертайте коди помилок.
... є прекрасною порадою, коли ви знаходитесь у кодовій базі, яка зазвичай не використовує коди помилок. Чому? Тому що ніхто не пам’ятатиме, щоб зберегти повернене значення та перевірити ваші коди помилок. Це все ще прекрасна умова, коли ти в С.
OneOf
Ви використовуєте ще одну умову. Це добре, якщо ви встановлюєте конвенцію, а не просто бороєтесь з іншою. В одній базі коду заплутано дві конвенції про помилки. Якщо ви якось позбулися всього коду, який використовує інший конвент, тоді продовжуйте.
Мені подобається конвенція сама. Одне з найкращих пояснень цього я знайшов тут * :
Але наскільки мені це подобається, я все одно не збираюся його змішувати з іншими умовами. Виберіть один і дотримуйтесь його. 1
1: Під чим я маю на увазі, не змушуйте мене думати про більш ніж одну конвенцію одночасно.
Наступні думки:
З цього обговорення я зараз забираю:
- Якщо ви очікуєте, що більша частина часу негайний абонент може зловити та обробляти виняток і продовжити свою роботу, можливо, через інший шлях, він, ймовірно, повинен бути частиною типу повернення. Тут необов'язково або OneOf може бути корисним тут.
- Якщо ви очікуєте, що безпосередній абонент не вловлює виняток більшу частину часу, киньте виняток, щоб зберегти безглуздість вручну передавати його в стек.
- Якщо ви не впевнені, що збирається зробити безпосередній абонент, можливо, надайте і те, і інше, як Parse та TryParse.
Це насправді не так просто. Одне з фундаментальних речей, яке вам потрібно зрозуміти - це нуль.
Скільки днів залишилось у травні? 0 (бо це не травень. Це вже червень).
Винятки - це спосіб відхилити припущення, але це не єдиний спосіб. Якщо ви використовуєте винятки для відхилення припущення, ви залишаєте щасливий шлях. Але якщо ви вибрали значення, щоб відправити вниз щасливий шлях, який сигналізує про те, що все не так просто, як передбачалося, ви можете залишатися на цьому шляху, поки він може мати справу з цими значеннями. Іноді 0 вже використовується для того, щоб щось означати, тому вам доведеться знайти інше значення для відображення вашої припущення, що відкидає ідею. Ви можете розпізнати цю ідею з її використання в старої доброї алгебри . Монади можуть допомогти у цьому, але це не завжди має бути монадою.
Наприклад 2 :
IList<int> ParseAllTheInts(String s) { ... }
Чи можете ви придумати якусь вагому причину, щоб це було розроблено так, щоб воно навмисно кидало що-небудь коли-небудь? Здогадайтесь, що ви отримуєте, коли жоден інт не може бути розбір? Мені навіть не потрібно говорити тобі.
Це знак доброго імені. Вибачте, але TryParse - це не моя ідея хорошого імені.
Ми часто уникаємо виключення, коли нічого не отримуємо, коли відповідь може бути одночасно більше, але чомусь, якщо відповідь - це одна річ або нічого, ми одержимо, наполягаючи, що це дає нам одне або кинути:
IList<Point> Intersection(Line a, Line b) { ... }
Чи справді паралельні лінії повинні викликати виняток? Невже це погано, якщо цей список ніколи не буде містити більше одного пункту?
Можливо, семантично ви просто не можете цього сприймати. Якщо так, то шкода. Але, можливо, Монади, які не мають довільного розміру, як List
це, змусять вас почувати себе краще.
Maybe<Point> Intersection(Line a, Line b) { ... }
Монади - це невеликі колекції спеціального призначення, які призначені для використання певними способами, які не потребують їх тестування. Ми повинні знайти способи поводження з ними незалежно від того, що вони містять. Таким чином щасливий шлях залишається простим. Якщо ви розтріскаєтесь і випробовуєте кожну монаду, яку торкаєтесь, ви їх неправильно використовуєте.
Я знаю, це дивно. Але це новий інструмент (ну, для нас). Тому дайте трохи часу. Молотки мають більше сенсу, коли ви перестаєте їх використовувати на саморізах.
Якщо ви побалуєте мене, я хотів би звернутися до цього коментаря:
Чому жодна з відповідей не пояснює, що монада Ефіра не є кодом помилки, і не є OneOf? Вони принципово різні, і, отже, питання, як видається, засноване на непорозумінні. (Хоча в модифікованій формі, це все-таки є актуальним питанням.) - Конрад Рудольф 4 червня 18 о 14:08
Це абсолютно правда. Монади набагато ближче до колекцій, ніж винятки, прапори чи коди помилок. При розумному використанні вони роблять прекрасні контейнери для таких речей.
Result
;-)