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


26

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

Нули є злими і їх слід уникати, коли це можливо .

Але, але - від моєї наївності, дозвольте мені кричати - іноді цінність МОЖЕ бути значимо відсутньою!

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

Після кожного повороту сервер транслює список оновлень для JS-коду на стороні клієнта. Клас оновлення:

public class GameUpdate
{
    public Player player;
    // other stuff
}

Цей клас серіалізується в JSON та надсилається підключеним програвачам.

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

Звичайно, я міг би "виправити" цей код, використовуючи спадкування:

public class GameUpdate
{
    // stuff
}

public class GamePlayerUpdate : GameUpdate
{
    public Player player;
    // other stuff
}

Однак я не бачу, як це стосується будь-якого покращення з двох причин:

  • Код JS тепер просто отримає об'єкт без програвача, як визначене властивість, що таке саме, як якщо б воно було недійсним, оскільки обидва випадки вимагають перевірити, чи є значення присутнім чи відсутнім;
  • Якщо до класу GameUpdate буде додано ще одне нульове поле, я повинен був би мати можливість використовувати багатократне успадковування, щоб продовжувати це рішення - але ІМ є злом сам по собі (на думку досвідчених програмістів) і, що ще важливіше, C # не робить це так, що я не можу ним користуватися.

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

Чи можете ви пояснити мені це питання?


2
Це питання специфічно для JavaScript? Для багатьох мов для цього потрібен необов'язковий тип, але я не знаю, чи є у js (хоча ви можете зробити його) або чи він би працював з json (Хоча це одна частина кепських нульових перевірок, а не весь ваш код
Річард Tingle

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

1
@usr - я повністю згоден. Нулл - твій друг. IMHO, немає більш чіткого способу представити відсутні значення. Він може допомогти вам знайти помилок, він може допомогти вам впоратися з помилками, він може допомогти вам просити прощення (спробувати / зловити). Що не подобається ?!
Скуба Стів

4
одне з перших правил проектування програмного забезпечення - «Завжди перевіряйте наявність нуля». Чому це навіть питання, для мене нудьгує.
МЕТЕ

1
@MattE - Абсолютно. Навіщо мені розробляти схему дизайну, коли насправді Null - це добре мати в базовій панелі інструментів. Те, що пропонує Грем, IMHO, вражає мене як надмірну інженерію.
Скуба Стів

Відповіді:


20

Багато речей краще повернути, ніж нульові.

  • Порожній рядок ("")
  • Порожня колекція
  • "Необов'язковий" або "може" монада
  • Функція, яка тихо нічого не робить
  • Об’єкт, повний методів, які тихо нічого не роблять
  • Змістовне значення, яке відкидає неправильну передумову питання
    (саме це змусило вас вважати в першу чергу нульовим)

Трюк - це зрозуміти, коли це зробити. Null дійсно добре роздуває обличчя, але тільки тоді, коли ви його відключите, не перевіряючи. Недоцільно пояснювати, чому.

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

Монади звучать химерно, але тут все, що вони роблять, дає вам зрозуміти, що дійсні розміри для колекції - 0 і 1. Якщо у вас є, врахуйте їх.

Будь-яка з них робить кращу роботу, щоб зробити ваш намір зрозумілим, ніж нульовий. Це найважливіша відмінність.

Це може допомогти зрозуміти, чому ви взагалі повертаєтесь, а не просто кидаєте виняток. Винятки - це по суті спосіб відкинути припущення (вони не єдиний спосіб). Коли ви запитуєте точку, де перетинаються дві лінії, ви припускаєте, що вони перетинаються. Вони можуть бути паралельними. Чи варто викидати виняток "паралельні лінії"? Ну ви могли, але тепер вам доведеться попрацювати з цим винятком в іншому місці або дозволити йому зупинити систему.

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

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

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


У своєму улюбленому експерименті з нульовою думкою я прошу вас уявити складну машину Rube Goldbergian, яка передає кодовані повідомлення за допомогою кольорового світла, вибираючи кольорові лампочки з конвеєра, вкручуючи їх у розетку та вмикаючи їх. Сигнал управляється різними кольорами лампочок, які розміщуються на стрічці конвеєра.

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

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

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

Ось поліморфний розчин. Системі навіть не потрібно знати, що щось змінилося.


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

Скільки яблук ви будете мені завдячувати, якщо я дам вам 1 яблуко? -1. Тому що я заборгував тобі 2 яблука з вчорашнього дня. Тут припущення щодо питання "ти мені зобов'язаний" відкидається з від'ємним числом. Незважаючи на те, що питання було неправильним, в цій відповіді закодована змістовна інформація. Відкидаючи це змістовно, питання не змушене змінюватись.

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

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

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

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

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

Мені це подобається, але лише тоді, коли конвенція чітко передається певним чином. Це не повинен бути спосіб ненавидіти новачків. Але навіть якщо ви користуєтесь цим, null все ще не найкращий спосіб зробити це. Нуль - це нічого небезпечного . Null робить вигляд, що ти можеш скористатись до тих пір, поки не використаєш його, тоді він пробурить діру у Всесвіті (розбиває вашу семантичну модель). Якщо ви не просвердлите діру у Всесвіті - це поведінка, яка вам потрібна, чому ви з цим вадитесь? Використовуйте щось інше.


9
"повернути колекцію, якщо в ній є лише 0 або 1 елемент" - Вибачте, але я не думаю, що я її отримую :( З мого POV, що робить це (a) додає зайві недійсні стани до класу (b), потрібно документувати, що колекція повинна містити щонайменше 1 елемент (c) все одно не вирішує проблему, оскільки перевірка, якщо колекція містить єдиний її елемент, перш ніж це все одно потрібно, інакше буде викинуто виняток?
gaazkam

2
@gaazkam бачити? Я вам казав, що це клопоти людей. Але це саме те, що ви робите щоразу, коли використовуєте порожній рядок.
candied_orange

16
Що відбувається, коли є дійсне значення, яке є порожнім рядком або встановленим?
Blrfl

5
У будь-якому випадку @gaazkam, ви не чітко перевіряєте, чи є в колекції елементи. Цикл (або відображення) у порожньому списку - це безпечна дія, яка не впливає.
RubberDuck

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

15

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

Давайте розберемося, як краще вирішити цю проблему.

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

Існують мови програмування, які забезпечують безпеку проти нульових покажчиків (так звана порожнеча безпека) під час компіляції. Наведіть кілька сучасних прикладів: Котлін, Руст, Свіфт. У цих мовах проблема відсутності інформації була сприйнята серйозно, і використання нуля там абсолютно безболісне - компілятор зупинить вас від перенаправлення нульових покажчиків.
У Java докладаються зусилля, щоб встановити анотації @ Nullable / @ NotNull разом із плагінами компілятора + IDE, щоб досягти такого ж ефекту. Це було не дуже вдало, але це не в можливості для цієї відповіді.

Явний, загальний тип, який позначає можливу відсутність, - це ще один спосіб боротьби з цим. Наприклад, у Java є java.util.Optional. Це може працювати:
На рівні проекту чи компанії ви можете встановити правила / рекомендації

  • використовувати лише якісні сторонні бібліотеки
  • завжди використовувати java.util.Optionalзамістьnull

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


Не могли б ви детально розібратися, як Java-опції кращі за нульові? Коли я читаю документацію , намагання отримати значення з необов’язкового створює виняток, якщо значення відсутнє; тож, здавалося б, ми там же: чек необхідний, і якщо ми його забудемо, то нас накрутили?
gaazkam

3
@gaazkam Ви маєте рацію, це не забезпечує безпеку часу компіляції. Крім того, сама Факультативна може бути недійсною, інше питання. Але це явно (порівняно зі змінними, які, можливо, є null / doc коментарями). Це дає лише реальну користь, якщо для всієї бази коду встановлено правило кодування. Ви не можете встановлювати речі недійсними. Якщо інформація може бути відсутнім, використовуйте Необов’язково. Це зумовлює явність. Звичайні нульові перевірки потім стають дзвінками доOptional.isPresent
1818 року


10

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

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

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

З null ви отримуєте присмак під час виконання, коли ви отримуєте присмак під час компіляції з якоюсь безпечною для типу альтернативою.

Не зовсім вірно, я можу отримати попередження про неперевірку нуля перед використанням змінної. І це не те саме. Я, можливо, не хочу шкодувати ресурси об'єкта, який не має мети. І що ще важливіше, мені доведеться перевірити альтернативу так само, щоб отримати бажану поведінку. Якщо я забуду це зробити, я скоріше отримаю NullReferenceException під час виконання, ніж якусь невизначену / ненавмисну ​​поведінку.

Потрібно пам’ятати про одне питання. У багатопотоковому контексті вам доведеться захиститись від перегонових умов, призначивши спочатку локальну змінну перед тим, як здійснити перевірку на null.

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

Ні, це нічого не повинно означати, щоб нічого не руйнувати. Ім'я та тип змінної / аргументу завжди вказуватимуть те, чого немає, тобто все, що потрібно знати.

Редагувати:

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

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

Будь-яка концепція може використовуватися неправильно. З того місця, де я стою, представлені "рішення" не є альтернативою для відповідного використання нуля. Вони - поняття з іншого світу.


9
Нуль руйнує знання про те, що нічого такого не представляє. Не існує нічого такого, що слід тихо ігнорувати. Нічого такого, що потрібно для зупинки системи, щоб уникнути недійсного стану. Нічого такого, що означає, що програміст накрутив і потребує виправлення коду. Нічого такого, що означає, що користувач попросив те, що не існує, і про це потрібно ввічливо сказати. Вибач, ні. Усі вони були закодовані як нульові. Через це, якщо ви кодуєте будь-яке з них як нулеве, тоді ви не повинні дивуватися, якщо ніхто не знає, що ви маєте на увазі. Ти змушуєш нас здогадуватися з контексту.
candied_orange

3
@candied_orange Ви можете зробити такий самий аргумент щодо будь-якого необов'язкового типу: Чи Noneпредставляє….
Дедуплікатор

3
@candied_orange Я все одно не розумію, що ти маєш на увазі. Різні види нічого? Якщо ви використовуєте null, коли, можливо, вам слід було б використати перерахунок? Існує лише один вид нічого. Причина того, що щось не може бути різним, але це не змінює ніщо, ані відповідну реакцію на те, що щось є нічим. Якщо ви очікуєте значення в магазині, якого немає, і це примітно, ви кидаєте або входите в журнал, коли ви запитуєте і нічого не отримуєте, ви не передаєте null як аргумент методу, а потім у цьому методі намагайтеся розібратися чому ти нуль. Нуль не була б помилкою.
Мартін Мейт

2
Null - це помилка, тому що у вас був шанс закодувати значення нічого. Ваше розуміння нічого не вимагає негайного поводження з нічим. 0, null, NaN, порожня рядок, порожній набір, void, нульовий об'єкт, не застосовується, не застосовується виняток, нічого не існує у багатьох-багатьох-багатьох формах. Вам не потрібно забувати те, що ви зараз знаєте, тільки тому, що ви не хочете мати справу з тим, що знаєте зараз. Якщо я попрошу вас взяти квадратний корінь -1, ви можете викинути виняток, щоб повідомити, що це неможливо, і забути все, або вигадати уявні числа і зберегти те, що ви знаєте.
candied_orange

1
@MartinMaat ви здаєтеся, що функція повинна створювати виняток кожного разу, коли ваші очікування порушуються. Це просто неправда. Часто трапляються кутові справи. Якщо ви насправді не можете побачити це, прочитайте це
candied_orange

7

Твоє запитання.

Але, але - від моєї наївності, дозвольте мені кричати - іноді цінність МОЖЕ бути значимо відсутньою!

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

Нули є злими і їх слід уникати, коли це можливо.

Я думаю, що це цілеспрямована, але надто генералізована заява.

Нули не є злими. Вони не є антипатерном. Вони не те, чого слід уникати будь-якою ціною. Однак нулі схильні до помилок (від невдачі перевірки на null).

Але я не розумію, як невдача перевірки на null є якимось доказом того, що null суттєво неправильно використовувати.

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

На решту цієї відповіді я збираюсь адаптувати намір "нулі злі" до більш нюансованих "з нулями слід поводитися відповідально ".


Ваше рішення.

Хоча я згоден з вашою думкою щодо використання нуля як глобального правила; Я не згоден з вашим використанням нуля в даному конкретному випадку.

Підсумовуючи нижченаведені відгуки: ви нерозумієте мету успадкування та поліморфізм. Хоча ваш аргумент щодо використання null має дійсність, ваш подальший "доказ" того, чому інший спосіб поганий, будується на неправильному використанні спадщини, що робить ваші бали дещо невідповідними нульовій проблемі.

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

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

Розглянемо, наприклад, повідомлення. У вас є повідомлення про помилки та повідомлення в чаті. Але хоча вони можуть містити дуже схожі дані (рядкове повідомлення та відправник), вони поводяться не так. Їх слід використовувати окремо, як різні класи.

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

  • Код JS тепер просто отримає об'єкт без програвача, як визначене властивість, що таке саме, як якщо б воно було недійсним, оскільки обидва випадки вимагають перевірити, чи є значення присутнім чи відсутнім;

Ваш аргумент спирається на помилки поліморфізму. Якщо у вас є метод , який повертає GameUpdateоб'єкт, він взагалі не повинен коли - небудь піклуватися , щоб розрізняти GameUpdate, PlayerGameUpdateчи NewlyDevelopedGameUpdate.

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

Це робить ваш аргумент суперечливим. У хорошій практиці ніколи не слід дбати про те GameUpdate, чи є у вашого об’єкта плеєр. Якщо ви намагаєтеся отримати доступ до Playerвласності сайту GameUpdate, ви робите щось нелогічне.

Введені мови, такі як C #, навіть не дозволять вам спробувати доступ до Playerвласності, GameUpdateоскільки GameUpdateпросто немає цього властивості. Javascript значно слабкіший у своєму підході (і він не вимагає попередньої компіляції), тому він не турбує вас виправляти, і він замість цього підірветься під час виконання.

  • Якщо до класу GameUpdate буде додано ще одне нульове поле, я повинен був би мати можливість використовувати багатократне успадковування, щоб продовжувати це рішення - але ІМ є злом сам по собі (на думку досвідчених програмістів) і, що ще важливіше, C # не робить це так, що я не можу ним користуватися.

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


Як уникнути проблем із нулем взагалі?

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

1. Використовуйте null все одно.

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

2. Використовуйте ненулеве безглузде значення

Простий приклад тут indexOf():

"abcde".indexOf("b"); // 1
"abcde".indexOf("f"); // -1

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

Логічно це може бути використано лише у випадках, коли тип повернення дозволяє отримати більше можливих значень, ніж може бути змістовно повернутий (indexOf = додатні цілі числа; int = від’ємні та додатні цілі числа)

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

Зауважте, що вам не слід покладатися на заздалегідь задані значення рядків, якщо ви не контролюєте значення рядка. Ви можете розглянути можливість встановлення імені користувача на "null", коли ви хочете повернути порожній об'єкт, але у вас виникнуть проблеми, коли Christopher Null підписується на вашу послугу .

3. Накиньте натомість виняток.

Ні. Просто ні. Виключення не повинні оброблятися для логічного потоку.

При цьому, є випадки, коли ви не хочете приховувати виняток (nullreference), а радше показуєте відповідний виняток. Наприклад:

var existingPerson = personRepository.GetById(123);

Це може кинути PersonNotFoundException(або подібне), коли немає людини з посвідченням 123.

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


5

Суть, чому нулі погані, полягає не в тому, що відсутнє значення кодується як нулеве, а в тому, що будь-яке опорне значення може бути нульовим. Якщо у вас є C #, ви, ймовірно, все одно втратили. Я не бачу крапки, щоб використовувати там деякі необов'язкові, оскільки тип посилання вже є необов'язковим. Але для Java є анотації @Notnull, які, здається, ви можете встановити на рівні пакету , тоді для значень, які можна пропустити, ви можете використовувати примітку @Nullable.


Так! Проблема - безглуздий дефолт.
Дедуплікатор

Я не погоджуюсь. Програмування в стилі, який уникає нуля, призводить до того, що менша кількість контрольних змінних буде нульовою, що призводить до меншої кількості змінних, які не повинні бути нульовими. Це не 100%, але це, можливо, 90%, що вартує ІМО.
Відновіть Моніку

1

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

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

Я думаю, що існує багато вагомих аргументів проти нуля:

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

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

  • тощо ...

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

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


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

1
@NicolBolas, ви праві, але першоджерело - це не поради, які цитують більшість людей, це передане повідомлення. Я хотів зробити лише те, що багато програмістів сказали: "ніколи не робіть X, X зло / погано / що завгодно", і, як ви кажете, пропускає багато застережень. Можливо, їх відкинули, можливо, їх ніколи не було, кінцевий результат той самий.
drjpizzle

0

У Java є Optionalклас із явним ifPresent+ лямбда для безпечного доступу до будь-якого значення. Це можна зробити і в JS:

Дивіться це питання StackOverflow .

До речі: багато пуристів вважають це використання сумнівним, але я не думаю, що так. Необов’язкові функції існують.


Посилання а не може java.lang.Optionalбути nullзанадто?
Дедуплікатор

@Deduplicator Ні, Optional.of(null)викинув би виняток, Optional.ofNullable(null)дав би Optional.empty()- не-значення.
Joop Eggen

І Optional<Type> a = null?
Дедуплікатор

@Deduplicator вірно, але там вам слід скористатися Optional<Optional<Type>> a = Optional.empty();;) - Іншими словами в JS (та інших мовах) такі речі не будуть здійсненні. Скала зі значеннями за замовчуванням зробила б. Ява, можливо, із перерахунком. JS Сумніваюся: Optional<Type> a = Optional.empty();.
Joop Eggen

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

0

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

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

Щоб зламати цю фундаментальну відсутність виразності, деякі спільноти (наприклад, громада Scala) не використовують нуль. Якщо у Scala ви хочете нульове значення типу T, ви берете аргумент типу Option[T], і якщо ви хочете нерегульоване значення, ви просто приймете T. Якщо хтось передасть нуль у ваш код, ваш код підірветься. Однак вважається, що викликовий код - це баггі-код. Optionє досить приємною заміною для null, тому що загальні шаблони обробки нулів витягуються у функції бібліотеки, як .map(f)або .getOrElse(someDefault).

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