Чи хороша практика мати особливе значення "ВСІ" у перерахунку


11

Я розробляю нову послугу в середовищі мікропослуг. Це послуга REST. Для простоти скажімо, що шлях є: / historyBooks

І метод POST для цього шляху створює нову книгу історії.

Припустимо, що книга історії охоплює одну чи кілька епох історії.

Для стислості припустимо, що у нас є лише такі епохи людської історії:

  • Стародавній
  • Повідомлення класичного
  • Сучасний

У своєму коді я хотів би представити їх у enum.

Тіло методу (корисна навантаження) у форматі JSON і повинно містити назву поля eras. Це поле - це список eraцінностей, які охоплює ця книга.

Тіло може виглядати так:

{
  "name": "From the cave to Einstein - a brief history review",
  "author": "Foo Bar",
  "eras": ["Ancient", "Post Classical", "Modern"]
}

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

В огляді API було внесено пропозицію:
Включіть інше значення ALLдля перерахунку епох, щоб явно вказати, що всі епохи охоплені.

Я думаю, що це має деякі плюси і мінуси.

Плюси:

Явне введення

Мінуси:

Якщо в списку вказано два пункти, скажіть ALLі Ancient- що буде взято з програми? Я думаю, це ALLповинно перевищувати інші значення, але це нова логіка бізнесу.

Якщо я запускаю запит щодо книг, які охоплюють певні епохи, як я б представляв книги, що охоплюють усі епохи? Якщо ALLтакож використовується для виводу (використовуючи ту саму логіку), то відповідальність споживача тлумачити ALLяк ["Ancient", "Post Classical", "Modern"].

Моє запитання

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

Що ти думаєш? Ви додали б це ALLзначення чи збережете API без нього?


7
Що станеться, якщо ви вирішите ви хочете додати атомну епоху? Чи книги, що охоплюють епохи "всіх", чарівно отримують новий зміст? Ви починаєте брехати про зміст книг? Ви проходите і все оновлюєте? Це може бути нетривіальним, якщо деякі книги насправді вже мають зміст про атомну епоху.
8bittree

Відповіді:


13

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

Як ви вказуєте, ВСЕ з іншими параметрами означає, що ви можете отримувати суперечливі запити. Ще одне зауваження полягає в тому, що ви можете передати "ВСІ", тоді будь-які інші суперечки стають виключеннями, а не включеннями, наприклад "ВСІ", "Древні" означають "Все, крім древніх". Це має певний сенс, але очевидно, що інтерфейс користувача повинен відображати те, що може бути надмірно складним.

TL; DR; Здебільшого "ВСЕ" - це сукупність інтерфейсу користувача, і ви можете досягти того ж на рівні сервісу без двозначності, тому не робіть цього.


6

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

Це, а також має спеціальний випадок "ВСІ", є поганим IMHO - ваші API повинні бути явними, де це можливо. Наявність спеціальних випадків типу "нічого насправді нічого не означає" означає, що кожен, хто споживає ваш API, також повинен знати всі ваші особливі випадки, або, як у випадку "ALL", призводити до запитів, у яких наміри та / або результати неоднозначні.

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


1

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

Це здається, що у вас є дворівневий критерій пошуку за часовий період:

  1. булева, is_selective?
  2. множина, диз'юнкція конкретних категорій

Абонент може вказати (помилковий, ігнорований) або (справжній, {'древній', 'сучасний'}).

Або ви можете вибрати інтерпретацію порожнього набору як підстановку, це позначає Не вибірково.

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


1

tl; dr Що
стосується Вашого актуального запитання - стосовно локальних структур даних при впровадженні - я погоджуюся з Вашим висновком: уникайте особливих цінностей усіх епох, оскільки це здебільшого додає складності та можливостей зробити помилки. Однак, особливо користувальницький інтерфейс користувача та, можливо, серіалізовані дані про корисне навантаження, можуть бути різними історіями.

Локальні структури даних

Давайте розглянемо це з точки зору системної системи. В основному, enum - це тип, який визначає суперечливий набір конкретних категорій (нумераторів), як уже вказувалося у відповіді @JH . Змінна типу enum містить саме одну з цих категорій. Якщо ви хочете представити колекцію значень перелічувача, вам потрібен другий тип:

// C++-inspired pseudo-code
enum Era { ancient, post_classical, modern };
using Eras = Collection<Era>;

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

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

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

Якщо в списку вказано два пункти, скажімо, ALL і Ancient - що буде взято з програми? [...]

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

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

Серіалізовані дані (JSON)

Спочатку у мене був цілий розділ про модифікацію запитів лише для читання та різних ролей для "eras"списку. Але врешті-решт я видалив її через KISS. Правила перерахування / збору не є необґрунтованими для даних JSON, тому дотримання речей є найпростішим рішенням.

Інтерфейс користувача для кінцевого користувача

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


1

Я думаю, що наявність нового ВСЕ викликає більше плутанини, ніж його взагалі немає.

Так.

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

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

Я б перевернув це догори ногами: ця книга не охоплює конкретної епохи.

Це також відповідає з точки зору запиту:

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

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


0

Нещодавно я реалізував базовий планувальник, і як уже згадувалося @LoztInSpace, більшість із них - це цукор UI.

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

У моєму випадку у мене є вихідні дні. Окрім "Усі", я додав опції "Робочі дні" та "Вихідні дні". Вибір кожної опції включить її в наступне використання, і вам доведеться вручну скасувати вибір у будь-який день, який ви не хочете включити.

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

Не використовуйте одну частину параметрів, щоб все включити, і одночасно виключити щось, щоб уникнути конфліктів і переконатися, що інтерфейс користувача має сенс для користувача.

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


0

Мені подобається ідея явно переносити ВСІ перерахунки, але я вважаю за краще встановити їх як прапори

const enum = {
    ALL: { value: 0 },
    ANCIENT: { name: 'Ancient', value: 1 },
    POST_CLASSICAL: { name: 'Post Classical', value: 2 },
    MODERN: { name: 'Modern', value: 4 }
}

Використовуючи бібліотеку на зразок флангону або вручну граючи з побітними операціями, коли вибрані всі значення, ви використовуєте ВСЕ.

Майте на увазі, що значення перерахунків завжди повинні бути подвійними у попередника. І щоб перевірити здоровість, ви ніколи не повинні видаляти перерахунок, хоча ви можете його відключити, налаштовуючи свій код, щоб завжди рахувати інвалідів як частину ВСІХ.

Хоча я мав би прапор NONE, і нехай клієнт вирішить, що робити, коли NONE використовується, якщо бізнес зміниться пізніше.

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

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