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


169

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

По-перше , якщо за заявою має супроводжуватися інше твердження, чи є в нього щось включити чи ні. Що призводить до вигляду коду таким чином:

if(condition) 
{
    doStuff();
    return whatever;
}
else
{
}

По-друге , краще перевірити на справжні значення, а не на хибні. Це означає, що краще протестувати змінну 'doorClosed' замість змінної '! DoorOpened'

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

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

if(condition)
{
}
else
{
    doStuff();
    return whatever;
}

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

Отже, мої запитання: це добре / погано / "не має значення" практика? Це звичайна практика?



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

11
Що говорить посібник зі стилю коду вашої компанії?
Thorbjørn Ravn Andersen

4
Часткова відповідь на ваше "Друге" питання. Якщо один із блоків дії довгий, а другий короткий, протестуйте умову, яка спочатку ставить короткий блок, щоб менше читати код був пропущений під час читання коду. Ця умова може бути запереченням або перейменованим, щоб зробити його випробуванням на істинність.
Етан Болкер

9
Resharper мав би щось сказати своєму другові, в рядку "Ваш код зайвий і марний, чи хочете ви, щоб я його видалив / перефактував?"
BgrWorker

Відповіді:


184

Явний elseблок

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

Я навіть не згадую про те, що за ifтвердженням не обов'язково дотримується elseабо нічого, або нічого: за ним може бути також один або кілька elifs.

Наявність returnвисловлення погіршує ситуацію. Навіть якби у вас був дійсно код, який потрібно виконати в elseблоці, було б зручніше зробити це так:

if (something)
{
    doStuff();
    return whatever;
}

doOtherThings();
return somethingElse;

Це змушує код зайняти на два рядки менше і скасовує elseблок. Про це - все це.

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

if (something)
{
}
if (other)
{
}
else
{
}

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

Тест на true, а не наfalse

Друге правило може мати певний сенс, але не в його нинішній формі.

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

if (!this.IsMaster || (!this.Ready && !this.CanWrite))

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

Вищенаведений стан можна легко зрозуміти:

if (this.IsSlave || (this.Synchronizing && this.ReadOnly))

169
Порожні блоки завжди виглядають як помилки для мене. Це одразу приверне мою увагу, і я запитаю "Чому це тут?"
Нельсон

50
@Neil Відмова від умови не обов'язково потребує додаткового часу процесора. C, складений на x86, може просто поміняти інструкцію стрибка з JZ(Jump if Zero) if (foo)на JNZ(Jump if Not Zero) на if (!foo).
8bittree

71
" створити додаткові властивості з чіткими іменами ": Якщо ви не маєте більш глибокого аналізу домену, це може змінити глобальну область застосування (оригінальний клас) для вирішення локальної проблеми (застосування конкретного правила бізнесу). Одне, що тут можна зробити, - це створити локальні булеви з потрібним станом, наприклад, "var isSlave =! This.IsMaster" і застосувати ваш if, якщо з локальними booleans, замість створення декількох інших властивостей. Отже, скористайтеся порадами із зерном солі та пройдіть певний час, щоб проаналізувати домен, перш ніж створювати нові властивості.
Мачадо

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

15
@ Марк, що менш зрозуміло, навіть з коментарем, ніж все-таки сказати if (!commonCase) { handle the uncommon case },.
Пол

62

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

if(condition) 
{
    doStuff();
    return whatever;
}

doSomethingElse(); // no else needed
return somethingelse;

Щодо другого пункту, добре уникати булевих імен, які містять мінус:

bool doorNotOpen = false; // a double negative is hard to reason
bool doorClosed = false; // this is much clearer

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

if(!condition)
{
    doStuff();
    return whatever;
}

120
Отже ... можна сказати, що подвійний негатив - це ні-ні? ;)
Пацуан

6
@Patsuan, дуже розумний. Мені це подобається. :)
Давид Арно

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

6
Зрозуміло, if (!doorNotOpen)це жахливо. Тож імена повинні бути максимально позитивними. if (! doorClosed)ідеально добре.
Девід Шварц

1
Що стосується вашого прикладу дверей, то два імені приклада необов'язково рівнозначні. У фізичному світі doorNotOpenце не те саме, doorClosedщо існує перехідний стан між відкритим і закритим. Я це бачу весь час у своїх промислових програмах, коли булеві стани визначаються фізичними датчиками. Якщо у вас є один датчик на відкритій стороні дверей, ви можете сказати, що двері відкриті чи не відкриті. Так само, якщо датчик знаходиться на закритій стороні, ви можете сказати лише закритий чи не закритий. Також датчик сам визначає, як підтверджується логічний стан.
Пітер М

45

1. Аргумент на користь порожніх elseтверджень.

Я часто використовую (і заперечую) щось подібне до цієї першої конструкції, порожнє інше. Це сигналізує читачам коду (як людського, так і автоматизованого інструментарію аналізу), що програміст вклав певну думку в ситуацію. Пропущені elseзаяви, які мали бути присутніми, загинули людей, розбилися транспортні засоби та коштували мільйони доларів. Наприклад, MISRA-C вимагає принаймні коментаря, в якому йдеться про те, що фінал, що відсутній, є умисним у if (condition_1) {do_this;} else if (condition_2) {do_that;} ... else if (condition_n) {do_something_else;}послідовності. Інші високі стандарти надійності йдуть ще далі: за кількома винятками, відсутні інші заяви заборонені.

Один виняток - простий коментар, щось уздовж /* Else not required */. Це сигналізує про той самий намір, що і три рядки порожні. Інший виняток, коли це порожнє ще не потрібно - це очевидно очевидний як для читачів коду, так і для автоматизованих інструментів аналізу, що це порожнє зайве. Наприклад, if (condition) { do_stuff; return; }аналогічно, порожнє інше не потрібно в разі throw somethingабо goto some_label1 замість return.

2. Аргумент переваги if (condition)над if (!condition).

Це пункт людських факторів. Складна булева логіка обтяжує багатьох людей. Навіть досвідченому програмісту доведеться думати if (!(complex || (boolean && condition))) do_that; else do_this;. Як мінімум, перепишіть це як if (complex || (boolean && condition)) do_this; else do_that;.

3. Це не означає, що слід віддавати перевагу порожнім thenвисловлюванням.

У другому розділі йдеться про "скоріше", а не "ти будеш". Це швидше керівництво, ніж правило. Причиною того, що цей принцип дає перевагу позитивним ifумовам, полягає в тому, що код повинен бути чітким і очевидним. Порожнє застереження (наприклад, if (condition) ; else do_something;) порушує це. Це заплутане програмування, змушує навіть самих досвідчених програмістів створити резервну копію та перечитати ifумову в її запереченому вигляді. Тож напишіть його в першу чергу в запереченій формі і опустіть інше твердження (або залиште порожнє або прокоментуйте цей ефект, якщо це буде дозволено).



1 я писав , що тоді становище , які закінчуються return, throwабо gotoне вимагає порожнього ще. Очевидно, що інша стаття не потрібна. А як же goto? Окрім того, критичні для безпеки правила програмування іноді забороняють раннє повернення і майже завжди забороняють кидати винятки. Однак вони дозволяють gotoу обмеженій формі (наприклад, goto cleanup1;). Таке обмежене використання gotoє кращою практикою в деяких місцях. Наприклад, ядро ​​Linux є такою повною частиною таких gotoоператорів.


6
!також легко не помітити. Це занадто лаконічно.
Thorbjørn Ravn Andersen

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

9
@Simon: типовою формою є else { /* Intentionally empty */ }щось подібне. Порожнє ще задовольняє статичний аналізатор, який бездумно шукає порушення правил. У коментарі повідомляється читачам, що порожнє ще навмисне. Знову ж таки, досвідчені програмісти зазвичай пропускають коментар як непотрібний, але не порожній. Програмування високої надійності - це власна область. Конструкти виглядають досить дивно для сторонніх. Ці конструкції використовуються через уроки в школі важких ударів, стукачі, які дуже важкі, коли життя і смерть або $$$$$$$$$$ під рукою.
Девід Хаммен

2
Я не розумію, наскільки порожні тоді пункти - це погана річ, коли порожні пропозиції не є (якщо вважати, що ми обираємо цей стиль). Я бачуif (target == source) then /* we need to do nothing */ else updateTarget(source)
Бергі

2
@ ThorbjørnRavnAndersen nope, notце ключове слово в C ++, як і інші побітові та логічні оператори. Див. Альтернативні представлення операторів .
Руслан

31

Я використовую порожню гілку else (а іноді і порожню if-гілку) в дуже рідкісних випадках: Коли очевидно, що і частиною if, і іншим слід якось обробляти, але з якихось нетривіальних причин справа може бути вирішена нічого не робити. І тому кожен, хто читає код з іншими необхідними діями, одразу підозрює, що щось не вистачає, і витрачає свій час.

if (condition) {
    // No action needed because ...
} else {
    do_else_action()
}

if (condition) {
    do_if_action()
} else {
    // No action needed because ...
}

Але ні:

if (condition) {
    do_if_action()
} else {
    // I was told that an if always should have an else ...
}

5
Це. Я вважаю, що твердження if test succeeds -> no action needed -> else -> action neededсемантично дуже відрізняється від твердження if it applies that the opposite of a test outcome is true then an action is needed. Мені пригадується цитата Мартіна Фаулера: "кожен може писати кодові комп'ютери, може зрозуміти; хороші програмісти пишуть код, які люди можуть зрозуміти".
Tasos Papastylianou

2
@TasosPapastylianou Це обман. Протилежне "якщо тест успішний -> не потрібно ніяких дій" - "якщо тест не вдався -> необхідні дії". Ви підклали її приблизно 10 словами.
Джеррі Б

@JerryB це залежить від "тесту". Іноді заперечення є (лінгвістично) прямолінійним, але іноді це не так, і може призвести до плутанини. У цих випадках зрозуміліше говорити про "заперечення / протилежне тому, щоб результат був істинним" проти "результат не був істинним"; однак справа в тому, що було б ще зрозумілішим ввести крок "порожній" або перевернути значення імен змінних. Це типово для ситуацій "де morgan": наприклад if ((missing || corrupt) && !backup) -> skip -> else do stuff, набагато зрозуміліше (+ інший намір), ніжif ((!missing && !corrupt) || backup) -> do stuff
Tasos Papastylianou,

1
@TasosPapastylianou Ви можете написати if(!((missing || corrupt) && !backup)) -> do stuffабо краще if((aviable && valid) || backup) -> do stuff. (Але в реальному прикладі ви повинні перевірити, чи є резервна копія дійсною перед її використанням)
12431234123412341234123

2
@TasosPapastylianou Де є подвійний негатив у тому, що я сказав? Звичайно, якби ви використовували некорумповані в якості імені змінної, то у вас буде чітка точка. Хоча, коли ви стикаєтесь до таких змішаних булевих операторів, можливо, краще мати тимчасові змінні, які слід використовувати в if: file_ok = !missing && !corrupt; if(file_ok || backup) do_stuff();що я особисто вважаю, це стає ще зрозумілішим.
Балдрікк

23

За інших рівних, віддайте перевагу стислість.

Те, що ви не пишете, ніхто не повинен читати і розуміти.

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


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

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

Корисною програмою явності було б розміщення коментарів щоразу, коли ви потрапляєте через випадки переключення // FALLTHRU, і використовувати коментар або порожній блок, де вам потрібно порожнє висловлювання for(a;b;c) /**/;.


7
// FALLTHRUТьфу, не в SCREAMING CAPS або з абревіатурами txtspk. В іншому випадку я згоден і сам дотримуюся цієї практики.
Коді Грей

7
@CodyGray - Це одне місце, де Зняття - хороша ідея. Більшість операторів комутації закінчують кожен випадок break. Якщо цього немає, breakце призвело до деяких вражаючих програмних збоїв. Коментар, який кричить читачам коду про те, що провал є навмисним, - це добре. Те, що інструменти статичного аналізу можуть шукати цю конкретну закономірність, а не скаржитися на провал через ситуацію, робить це ще кращою справою.
Девід Хаммен

1
@Wossname: Я щойно перевірив свою vim, і вона не визнає будь-яких варіантів FALLTHRU. І я думаю, що з поважної причини: TODOі FIXMEкоментарі - це коментарі, які потрібно розпізнати, тому що ви хочете мати можливість запитати, які з ваших git grepвідомих вад у вашому коді та де вони знаходяться. Провал - це не дефект, і немає жодного приводу жадіти за це, що коли-небудь.
cmaster

4
@DavidHammen Ні, кричати - це не дуже гарна ідея: якщо на місці очікуваного перерви у вас є // провал ;, цього має бути достатньо, щоб будь-який розробник зрозумів негайно. Також // провал не говорить про дефект, він просто сигналізує про те, що обставини були враховані, і що розрив; яка, здається, відсутня, насправді призначена не бути там. Це суто інформаційна мета, і ні про що кричати.
cmaster

1
@cmaster - Кричати - це не дуже гарна ідея - це ваша думка. Думки інших людей відрізняються. І те, що ваша конфігурація vim не розпізнає FALLTHRU, не означає, що інші люди не налаштували vim для цього.
Девід Хаммен

6

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

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


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

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

Особистий стиль: незалежно від того, який стиль дужки я використовую (Allman, 1TB, ...), я завжди використовую брекети.
Девід Хаммен

Складні умови або ті, які, на вашу думку, можуть бути важкими для розуміння, майже завжди можуть бути вирішені шляхом повторного врахування їх власних методів / функцій. Щось подібне if(hasWriteAccess(user,file))може бути складним внизу, але з першого погляду ви точно знаєте, яким повинен бути результат. Навіть якщо це лише декілька умов, наприклад, if(user.hasPermission() && !file.isLocked())виклик методу з відповідним іменем отримує точку зору, тому негативів стає менше проблемою.
Кріс Шнайдер

Домовились. Потім я знову великий фанат рефакторингу, коли це можливо :)
Langecrew

5

Явний elseблок

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

На ifмій погляд, твердження, насправді, охоплює дві різні функції.

Якщо нам належить щось зробити, зробіть це тут.

Такі речі, очевидно, не потребують elseчастини.

    if (customer.hasCataracts()) {
        appointmentSuggestions.add(new CataractAppointment(customer));
    }
    if (customer.isDiabetic()) {
        customer.assignNurse(DiabeticNurses.pickBestFor(customer));
    }

а в деяких випадках наполягати на додаванні elseможе ввести в оману.

    if (k > n) {
        return BigInteger.ZERO;
    }
    if (k <= 0 || k == n) {
        return BigInteger.ONE;
    }

це НЕ те ж саме,

    if (k > n) {
        return BigInteger.ZERO;
    } else {
        if (k <= 0 || k == n) {
            return BigInteger.ONE;
        }
    }

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

Якщо ми перевіряємо конкретний стан, часто корисно додати порожнє, elseщоб нагадати, щоб приховати цю подію

        // Count wins/losses.
        if (doors[firstChoice] == Prize.Car) {
            // We would have won without switching!
            winWhenNotSwitched += 1;
        } else {
            // We win if we switched to the car!
            if (doors[secondChoice] == Prize.Car) {
                // We picked right!
                winWhenSwitched += 1;
            } else {
                // Bad choice.
                lost += 1;
            }
        }

Пам'ятайте, що ці правила застосовуються лише тоді, коли ви пишете новий код . IMHO Порожні elseпропозиції повинні бути видалені перед реєстрацією.


Тест на true, а не наfalse

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

Хоча код, як

    if(!customer.canBuyAlcohol()) {
        // ...
    }

жартує читача, але робить його

    if(customer.canBuyAlcohol()) {
        // Do nothing.
    } else {
        // ...
    }

принаймні так само погано, якщо не гірше.

Я закодований в BCPL багато років тому , і в цій мові є IFпункт іUNLESS положення , щоб ви могли закодувати набагато більш читані , як:

    unless(customer.canBuyAlcohol()) {
        // ...
    }

що значно краще, але все ще не ідеально.


Мій особистий процес

Як правило, коли я пишу новий код, я часто додаю порожній elseблок до ifзаяви, щоб нагадати мені, що я ще не висвітлював цю подію. Це допомагає мені уникнути DFSпастки і гарантує, що при перегляді коду я помічаю, що робити ще багато чого. Однак я зазвичай додаю TODOкоментар, щоб відстежувати.

  if (returnVal == JFileChooser.APPROVE_OPTION) {
    handleFileChosen();
  } else {
    // TODO: Handle case where they pressed Cancel.
  }

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


Ваше звернення «порожніх» блоків читається як стандартний гасячи коду при реалізації thngs ...
Deduplicator

Цей unlessблок є гарною пропозицією і навряд чи обмежується BCPL.
tchrist

@TobySpeight На жаль. Це якось уникнуло моєї уваги, що те, що я написав, ...було насправді return. (Насправді, з k=-1, n=-2приймається перше повернення, але це багато в чому суперечить.)
Девід Річербі

Сучасний Perl має ifі unless. Більше того, він також дозволяє це після заяви, так що ви можете сказати print "Hello world!" if $born;або print "Hello world!" unless $dead;і те, і інше буде робити саме те, що ви думаєте, що вони роблять.
CVn

1

Для першого моменту я застосував мову, яка змусила використовувати оператори IF таким чином (в Опалі - мова за скребком екрану мейнфрейму для розміщення інтерфейсу GUI на мейнфрейм-системах), і лише один рядок для ІФ та ELSE. Це було не приємне враження!

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

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

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

Негативна логіка, як правило, менш читається, хоча і не стільки на такому простому твердженні IF.


2
Я бачу, так це було звичайною практикою в якийсь момент.
Пацуан

@Patsuan - так, певною мірою. Хоча це було тоді, коли асемблер мейнфрейму був серйозною альтернативою критичному коду продуктивності.
Кікстарт

1
"Давно ... було прийнято, що негативні перевірки менш ефективні". Ну, вони є , якщо компілятор оптимізує if !A then B; else Cдля if A then C; else B. Обчислення заперечення умови є додатковою інструкцією CPU. (Хоча, як ви кажете, це навряд чи буде значно менш ефективним.)
Девід Річербі

@DavidRicherby І якщо ми говоримо взагалі, деякі мови можуть зробити такі оптимізації справді важкими. Візьміть C ++ з перевантаженим !і ==операторами, наприклад. Не те, щоб хтось насправді це робив , пам'ятайте ...
CVn

1

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

Питання щодо уникнення негативів заслуговує на деяку увагу ще: Зазвичай, коли вам потрібно використовувати булеве значення, вам потрібні деякі блоки коду для роботи, коли воно встановлено, та деякі інші блоки для роботи, коли вони не встановлені. Таким чином, якщо ви застосовуєте правило без негативів, ви примушуєте або мати if() {} else {...}оператори (з порожнім ifблоком!), Або створювати другий булевий код для кожного булевого, що містить його заперечне значення. Обидва варіанти погані, оскільки вони бентежать ваших читачів.

Корисна політика полягає в наступному: ніколи не використовуйте заперечену форму в імені булевого і не висловлюйте заперечення як одиничне !. Висловлювання на кшталт if(!doorLocked)ідеально зрозуміле, вислів, як if(!doorUnlocked)мозок вузлів. Пізніший тип вираження - це те, чого потрібно уникати будь-якою ціною, а не наявність одиничного !в if()умові.


0

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


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

0

Існує ще одна модель, яка ще не згадується щодо обробки другого випадку, який містить порожній if-блок. Одним із способів впоратися з цим було б повернення в блок if. Подобається це:

void foo()
{
    if (condition) return;

    doStuff();
    ...
}

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


0

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

У функціональному стилі програмування ви маєте справу з виразами, а не з твердженнями. Отже, кожен блок коду має зворотне значення - включаючи if-then-elseвираз. Це, однак, виключатиме порожній elseблок. Дозвольте навести ваш приклад:

var even = if (n % 2 == 0) {
  return "even";
} else {
  return "odd";
}

Тепер для мов зі синтаксисом натхненного стилю C або на C, (наприклад, Java, C # та JavaScript, лише декілька), це виглядає дивно. Однак це виглядає набагато звичніше, коли він пишеться таким:

var even = (n % 2 == 0) ? "even" : "odd";

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


0

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

Я НЕ згоден , if (a) { } else { b(); }повинен бути переписаний , як if (!a) { b(); }і if (a) { b(); } else { }має бути переписаний , як if (a) { b(); }.

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

По-друге, краще перевірити на справжні значення, а не на хибні. Це означає, що краще протестувати змінну 'doorClosed' замість змінної '! DoorOpened'

У мене змішані почуття з цього приводу. Недолік наявності doorClosedта doorOpenedчи потенційно він подвоює кількість слів / термінів, про які вам потрібно знати. Ще один недолік - з часом сенс doorClosedі doorOpenedможе змінюватися (інші розробники, що вступають за вами), і у вас можуть з’явитися дві властивості, які вже не є точними запереченнями один одного. Замість того, щоб уникати заперечень, я ціную адаптацію мови коду (назви класів, назви змінних тощо) до лексики ділових користувачів та до вимог, які мені задано. Я б не хотів складати цілком новий термін, щоб уникнути цього!якщо цей термін має значення лише для розробника, який ділові користувачі не зрозуміють. Я хочу, щоб розробники розмовляли тією ж мовою, що і користувачі та вимоги, що пишуть люди. Тепер, якщо нові умови спростять модель, це важливо, але це потрібно вирішити до того, як вимоги будуть доопрацьовані, а не після. Розробка повинна вирішити цю проблему, перш ніж розпочати кодування.

Я схильний сумніватися в своєму інстинкті.

Добре продовжувати сумніватися.

Це хороша / погана / "не має значення" практика?

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

Це звичайна практика?

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


0

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

Однак це в деяких випадках розширений коментар, а не відповідь.

Коли це зазначено

По-друге, краще перевірити на справжні значення, а не на хибні. Це означає, що краще протестувати змінну 'doorClosed' замість змінної '! DoorOpened'

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

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

  1. Двері відчинені.
  2. Двері не є ні повністю відкритими, ні повністю закритими.
  3. Двері закриті.

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

  1. Якщо датчик знаходиться на відкритій стороні дверей: Дверцята відкрита або не відкрита
  2. Якщо датчик знаходиться на закритій стороні дверей: дверцята або закрита, або не закрита.

І не можна взаємозамінювати ці поняття через використання двійкового заперечення. Таким чином , doorClosedі !doorOpened, ймовірно , НЕ є синонімами і будь-яка спроба зробити вигляд , що вони є синонімами, помилково думати , що передбачає більшу знання стану системи , ніж на самому справі існує.

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


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

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

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

Прикладом другого може бути простий привід з моторним приводом. Ви гальмуєте рух двигуна вперед, коли привід повністю вимкнений. І ви гальмуєте рух двигуна в зворотному напрямку, коли привід повністю введений.

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


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

@Sanchises Висловлювання не суперечать один одному, оскільки ви вивели їх із контексту. Перше твердження полягає в тому, що два протилежних бінарних вимірювання потрійного стану не можуть бути взаємозамінні через бінарне заперечення. Друге твердження полягає в тому, що лише часткові знання про потрійний стан можуть бути достатніми для того, щоб програма правильно працювала. Що стосується дверей, то питання, що стосуються ОП, стосуються відкритих та закритих дверей. Я лише вказую на очевидне припущення, яке може вплинути на обробку даних.
Пітер М

-1

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

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

Як було сказано в іншому місці, наявність блоку else дуже корисно показати, що ви явно хочете, щоб нічого не сталося в іншому випадку. Зробивши багато функціональних програмувань (де ще потрібно), я дізнався, що завжди слід враховувати інше, коли пишеш if, хоча, як згадує @OldCurmudgeon, дійсно є два різних випадки використання. Треба мати інше, не слід. На жаль, це не те, про що ти завжди можеш сказати з першого погляду, не кажучи вже про підводку, отже, догматичний «завжди ставив інший блок».

Що стосується "ніяких негативів", знову ж таки, абсолютні правила погані. Зробити порожній, якщо це може бути дивно, особливо якщо це тип, якщо це не потрібно іншого, тому пишіть все це, а не! або an == false - це погано. Однак, існує маса випадків, коли негатив має сенс. Загальним прикладом може бути кешування значення:

static var cached = null

func getCached() {
    if !cached {
        cached = (some calculation, etc)
    }

    return cached
}

якщо фактична (ментальна / англійська) логіка передбачає негативне значення, то також слід заявляти if.

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