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


41

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

Це правильно робити? Якій меті це послужило б?


9
Це залежить від вашого ... Я маю на увазі стиль коду керівника.
SD

1
Я думаю, є навіть випадки, коли ви не хочете справи за замовчуванням. Подумайте про перемикання на перерахунок. Ваш перемикач визначає регістр для всіх можливих значень. Тепер додавання значення до enum має призвести до додавання регістру в цей комутатор. Якщо ви цього не зробили, це може бути помилкою, оскільки ви хочете зробити щось для цього нового значення перерахунку, але ваш код цього не робить. Ваш компілятор може попередити вас (я думаю, не впевнений у Java) про це, але лише у тому випадку, якщо ви не поставили справу за замовчуванням. Крім того, ви можете надрукувати попередження у випадку за замовчуванням, коли ви впевнені, що код ніколи його не досягає.
leemes

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

Відповіді:


31

Здається, є три випадки, коли defaultзаява не потрібна:

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

  2. ви знаєте, як і де switch caseбуде використовуватися заповіт і які значення будуть входити до нього. Знову ж це може змінитися, і може знадобитися додаткова обробка.

  3. інші випадки не потребують особливої ​​обробки. Якщо це так, я думаю, вас попросять додати default case, оскільки це прийнятий стиль кодування, і це робить ваш код більш читабельним.

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


14
У випадках 1 і 2 у випадку за замовчуванням має бути помилка. Якщо у вашому стилі кодування завжди використовується за замовчуванням, тоді зробіть це, але змусьте його видавати помилку в 1 і 2. Це можливо, він повинен видавати його під час компіляції, але це може бути не завжди можливо, якщо взагалі в Java. Я, принаймні, думаю, що користувачеві важливо отримати повідомлення про те, що "ти
стріляєш

11
Випадок за замовчуванням завжди є хорошою ідеєю, тому що, наприклад, у випадку перерахунків, якщо хтось додає до запису новий запис, ваш випадок за замовчуванням повинен робити щось подібне до того, throw new IllegalStateException("Unrecognised case "+myEnum)що показує вам, де і в чому помилка.
Багатий

Це означає, що ви завжди повинні мати elseзастереження після кожного, if/else ifнавіть якщо ви знаєте, що цього не повинно статися? Мені це не здається гарною ідеєю ...
jadkik94

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

28

Чи правильно це робити? Якій меті це послужило б?

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

Якщо ви вважаєте, що у вас switchвходять випадки для будь-якої можливої ​​умови, тоді добре зробити те, що ви ставите assertзаяву у випадку за замовчуванням. Таким чином, коли хтось змінить код і ненавмисно додасть умову, яку ваш switchне охоплює, він натисне assertі зрозуміє, що їм потрібно вирішити цю частину коду.

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


1
що таке assert?
ЛЕТАТИ

1
@FLY Це ключове слово на Java і зазвичай це макрос на C, C ++ тощо. Так чи інакше, твердження використовується для перевірки того, що деяке припущення залишається істинним, і кидати і виключати або інакше викликати помилку, якщо це не так.
Калеб

Я відредагував свій попередній коментар із посиланням на вікіпедію
FLY

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

12

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


1
+1 для вирішення проблеми під час компіляції, а не під час виконання.
SylvainD

Цікаво, як проголошується та приймається повністю оманлива відповідь. Швидке гуглювання показує, що компілятор java має таке попередження. Чому на землі ви будете чекати помилки під час виконання, люди?
Артур

Я не знав про таку поведінку. Мій зразок коду ideone ideone.com/WvytWq пропонує інше. Якщо припустити, що ви кажете, що це правда, що станеться, якщо хтось додасть значення для типу перерахування, але ви не перекомпілюєте свій код?
emory


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

9

Багато в чому це питання те саме, що часто запитують: Чи потрібно мені elseпункт в кінці an if/ else ifдрабинки, який охоплює кожен варіант .

Відповідь, синтаксично кажучи, ні, ви цього не робите. Але є все-таки ...

defaultРозділ може бути там в протягом (як мінімум) по двох причинах:

  1. Як обробник помилок - впевнений, сюди ніколи не слід потрапляти! - це вимога, яку можна зробити, але що робити, якщо це зробити? Пошкодження даних, або, що ще гірше, відсутність перевірки даних - це шляхи до відмови програми, якщо вони неправильно зафіксовані. У цьому випадку це не повинно бути порожнім пунктом!
  2. Покриття дизайну / коду - навіть у найпростішій формі діаграми є два маршрути із if-оператора та завжди otherwiseіз випадку. Немає жодної причини, щоб не включати їх у вихідний код.

Моя філософія завжди досить проста - оцініть найгірший сценарій з двох варіантів та йдіть до найбезпечнішого. У випадку порожнього elseабо defaultпункту найгіршими варіантами є:

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

Надмірний? Можливо ... але тоді моє програмне забезпечення може вбити людей, якщо воно піде не так. Я б краще не ризикував.


На відміну від рекомендацій MISRA-C {див. Профіль для належності} рекомендується defaultпункт для кожногоswitch


Не тільки в кінці драбини. Питання: чи потрібно мені elseпісля if. Але, як ви сказали, ні, це не так.
ott--

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

Хороший момент, @emory - це було не дуже зрозуміло, чи це було ... відредаговано :)
Ендрю

6

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

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

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

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

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


2

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


0

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

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

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


-2

Розглянемо

enum Gender
{
       MALE ,
       FEMALE ;
}

і

void run ( Gender g )
{
      switch ( g )
      {
           case MALE :
                 // code related to males
                 break ;
           default :
                 assert Gender . FEMALE . equals ( g ) ;
                 // code related to females
                 break ;
      }
}

Я можу стверджувати, що це чудово, ніж у випадку MALE, випадку FEMALE і випадку за замовчуванням, який видає виняток.

Під час фази тестування комп'ютер перевірить, чи g FEMALE. Під час виробництва перевірку буде опущено. (Так, мікрооптимізація!)

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


1
1. Немає необхідності в мікрооптимізації - усунення мертвого коду використовується в компіляторах протягом останніх 30 років і так воно буде усунене JIT. 2. 100% покриття коду, як правило, не вважається важливим (з одного боку, 100% покриття може не охоплювати всі крайові випадки, з іншого жоден тест не вловлює рядки assert_not_reached у правильній програмі). У цьому випадку відсутнє значення за замовчуванням призведе до помилки компілятора. З іншого боку - як би ви перевірили, чи працює арбітраж (ви пересунете проблему та приховуєте її на 100% покриття, замість того, щоб мати той самий рядок «я цього не відбудуться, обіцяю»).
Мацей П'єхотка

1
3. Коли Gender . FEMALE . equals ( FEMALE ) ;оцінюється false? Ви мали на увазі, Gender.FEMALE.equals (g)але оскільки ви можете залежати від того, щоб посилання на стабільність були стабільними, ви можете просто написати g == Gender.FEMALE. 4. (Особиста думка) такий код читати набагато складніше, і він створює дещо більш заплутані помилки.
Maciej Piechotka

@MaciejPiechotka гарний улов про g. Так, мікрооптимізація - це не користь, але це також не є недоліком. 100% покриття коду - це користь, яка є однією з ваших цілей, і ваш інструмент покриття коду не перевіряє твердження (чого не слід).
emory

Тоді чому б це було однією з моїх цілей? "Цікаву" частину слід перевірити на всі крайові випадки (незалежно від того, чи вони позначають рядки коду), а "нудні" частини можна просто пропустити, щоб заощадити час на тестування. Покриття коду IMHO є поганою метрикою тестів.
Maciej Piechotka
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.