Твоє запитання.
Але, але - від моєї наївності, дозвольте мені кричати - іноді цінність МОЖЕ бути значимо відсутньою!
Повністю на борту з вами там. Однак є декілька застережень, які я прийму за секунду. Але спочатку:
Нули є злими і їх слід уникати, коли це можливо.
Я думаю, що це цілеспрямована, але надто генералізована заява.
Нули не є злими. Вони не є антипатерном. Вони не те, чого слід уникати будь-якою ціною. Однак нулі схильні до помилок (від невдачі перевірки на 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.
Дещо очевидно, це прийнятно лише у випадках, коли не знаходження предмета унеможливлює подальшу обробку. Якщо неможливо знайти предмет, і додаток може продовжуватися, НІКОЛИ не кидайте виняток . Я не можу наголосити на цьому достатньо.