Чи варто кидати виняток у випадку значущого значення поза діапазону або самостійно впоратися з ним?


42

Я написав структуру, яка представляє координати широти / довготи. Їх значення коливаються від -180 до 180 для довготи і 90 до -90 для широт.

Якщо користувач цієї структури дає мені значення поза цим діапазоном, у мене є 2 варіанти:

  1. Викиньте виняток (аргумент поза діапазоном)
  2. Перетворити значення в обмеження

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

Чи краще кинути виняток, щоб сказати користувачеві, що його код дав мені значення, яке воно не повинно мати?

Редагувати: Я також знаю різницю між lat / lng та координатами, але я хотів спростити це для легшого обговорення - це не найяскравіші ідеї


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

C #, чи це має значення? Він не підтримує обмежень, якщо це саме ви маєте на увазі.
К. Гкініс

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

2
Я схильний триматися подалі від припущень щодо того, що "мав на увазі" користувач, передаючи певні значення параметрів, особливо тих, для яких мій код не задовольняє. Здається, схожий випадок.
JᴀʏMᴇᴇ

2
Координати Web Mercator не від -180 до 180 та -90 до 90. Це широта / довгота (а для цього є навіть кілька систем координат). Прогнози Меркатора, як правило, складаються в сотнях тисяч або мільйонів і мають одиниці "метрів" (навіть не суворо це, оскільки довжина кожної одиниці охоплює збільшення реальної відстані до землі, коли ви наближаєтесь до полюсів), а не градусів. Навіть з точки зору градусів вона обмежена до ± 85,051129 градусів, оскільки проекція стає нескінченно широкою на полюсах. (Я подав редагування, виправляючи це, оскільки це не суть питання.)
jpmc26

Відповіді:


51

Якщо суть вашого питання в цьому ...

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

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

Питання в тому, чи справді ви стикаєтеся з цією справою .

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

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

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


12
Довгота поза межами діапазону від -180 до +180, мабуть, вважається прийнятною, але широта поза межами діапазону від -90 до +90 здавалася б безглуздою. Як тільки людина досягне Північного або Південного полюса, подальше подорож на північ або південь не визначене.
supercat

4
@supercat Я схильний погоджуватися з вами, але оскільки я не знаю, які значення насправді є недійсними у специфікації Web Mercator, я намагаюся не робити жодних важких висновків. ОП знає проблемну область краще, ніж я.
Теодорос Чатзіґананакіс

1
@supercat: початок, де меридіан досягає екватора. подорожувати по меридіану відповідно до широти. Якщо широта> 90, то просто продовжуйте рухатись по тому самому великому колу. Нема проблем. Задокументуйте це, а решту залиште клієнту.
кевін клайн

4
@supercat Це гірше. У проекції Web Mercator ± 90 насправді проектується на нескінченну ширину. Тож стандарт насправді відключає його на рівні ± 85.051129. Крім того, lat / long! = Координати web mercator. Координати Меркатора використовують різницю метрів, а не градусів. (Коли ви наближаєтесь до полюсів, кожен "метр" насправді відповідає більшому та більшому шматочку землі.) Координати ОП мають суто лат / довгу. Web Mercator не має нічого спільного з ними, крім того, що вони можуть відображатись на базі основної карти Web Mercator, і якась просторова бібліотека проектується під кришкою для них.
jpmc26

1
Я повинен сказати "еквівалент ± 85.051129", оскільки це не фактична координата.
jpmc26

10

Це багато залежить. Але вам слід вирішити щось зробити і задокументувати .

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

У цьому випадку я можу бачити аргументи в будь-якому випадку. Якщо хтось подорожує +10 градусів від 175 градусів, він повинен закінчитися в -175. Якщо ви завжди нормалізуєте введення користувача і так вважаєте 185 як еквівалент -175, тоді клієнтський код не може зробити неправильну справу, коли він додає 10 градусів; це завжди має правильний ефект. Якщо ви трактуєте 185 як помилку, ви змушуєте кожен випадок, коли клієнтський код додає відносні градуси, щоб ввести логіку нормалізації (або, принаймні, пам'ятати, щоб викликати процедуру нормалізації), ви насправді викликаєтеклопи (хоча, сподіваємось, легко зловити тих, які швидко будуть розбиті). Але якщо номер довготи введений користувачем, записаний буквально в програмі, або обчислюється за допомогою якоїсь процедури, призначеної завжди бути в [-180, 180), то значення поза цим діапазоном, швидше за все, свідчить про помилку, тому "корисно "перетворення це може приховати проблеми.

Моїм ідеалом у цьому випадку, певно, було б визначити тип, який представляє правильний домен. Використовуйте абстрактний тип (не дозволяйте клієнтському коду просто отримувати доступ до необмежених номерів всередині нього) та надайте як нормалізуючий, так і підтверджуючий завод (щоб клієнт міг зробити компроміс). Але яке б значення цього типу не було, 185 слід відрізняти від -175, коли його бачитимуть через ваш публічний API (не важливо, чи вони перетворені на будівництві, чи ви надаєте рівність, аксесуари та інші операції, які якось ігнорують різницю) .


3

Якщо вам не дуже важливо вибрати одне рішення, ви можете просто дозволити користувачеві вирішити.

З огляду на те, що ваша структура є об'єктом лише для читання та створена методом / конструктором, ви можете надати два перевантаження на основі параметрів, які має користувач:

  • Викиньте виняток (аргумент поза діапазоном)
  • Перетворити значення в обмеження

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

Редагувати: на основі коментарів, я припускаю, що ви використовуєте c #.


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

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

2
@Snowman: Так, важко буде перевантажити конструктор одними і тими ж аргументами, але не буде складно мати два статичні методи та приватний конструктор.
wchargin

1
@ K.Gkinis: Мета конструктора, що викидає винятки, не "переконайтесь, що додаток помирає" - зрештою, клієнт завжди може бути catchвашими винятками. Як говорили інші, це дозволяє дозволити клієнту обмежувати себе, якщо він цього забажає. Ти насправді нічого не обходить.
wchargin

Ця пропозиція ускладнює код без користі. Або метод повинен перетворити недійсний вхід, або він не повинен. Наявність двох перевантажень робить код складнішим без вирішення дилеми.
ЖакБ

0

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

Введення через інтерфейс користувача

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

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

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

Найголовніше, ви повинні врахувати, чи коригування вводу навіть має користь для користувача. Чому користувач вводить недійсні дані? Неважко зрозуміти, як хтось може зробити орфографічну помилку, але навіщо хтось вводити довготу -185? Якби користувач дійсно мав на увазі +175, він, ймовірно, набере +175. Я думаю, що найімовірніше, що недійсна довгота просто є помилкою введення тексту, а користувач мав на увазі -85 або щось інше. У цьому випадку мовчки перетворення - це погано і не допомагає . Найбільш зручним для користувачів підходом для вашої програми, ймовірно, буде сповіщення користувача про недійсне значення та дозволити користувачеві виправити це самі.

Введення через API

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


0

Вам слід кинути виняток.

У прикладі, який ви подали, надсилаючи 185 і перетворюючи це на -175, тоді в деяких випадках може бути зручно надати цю функціональність. Але що робити, якщо абонент надсилає 1 мільйон? Чи справді вони мають намір це перетворити? Здається, більш вірогідною є помилка. Отже, якщо вам потрібно кинути виняток на 1 000 000, але не на 185, тоді вам доведеться прийняти рішення про довільний поріг для викидання виключення. Цей поріг коли-небудь відключить вас, коли деяка програма, що викликає, надсилає значення біля порогу.

Краще викинути виключення для значень поза діапазону.


-1

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

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

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


Як би ви надали помилки часу компіляції для введення користувача?
ЖакБ

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

Але тоді вам все одно доведеться вирішити, чи хочете ви відхилити введення чи перетворити на допустимий діапазон, тому це не дає відповіді на початкове запитання.
ЖакБ

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

Отже ... вам потрібно скласти новий клас, який містить вхідні дані та валідати. Коли ви побудуєте об'єкт класу з необроблених входів, що відбувається? Кинути виняток? Мовчки пройти? Ви просто пересуньте проблему.
djechlin

-1

За замовчуванням має бути кинути виняток. Ви також можете дозволити такий варіант, як strict=falseі зробити примус на основі прапора, де, звичайно strict=true, за замовчуванням. Це досить поширене:


Це ускладнює код без користі.
ЖакБ

@JacquesB викидає виняток за замовчуванням чи дозволяє strict=false?
djechlin

@JacquesB Я додав декілька прикладів API, які підтримують суворі та м'які режими, будь ласка, погляньте.
djechlin

-2

Для мене найкраща практика - ніколи не змінювати введення користувача. Я зазвичай використовую підхід, щоб відокремити перевірку від виконання.

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

"Користувачем", на яке йдеться питання, є розробник, що використовує API, а не кінцевий користувач ...
Jay Elston,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.