Что поместить в блок IF, а что - в блок ELSE?

Это второстепенный вопрос стиля, но важен каждый бит читабельности, который вы добавляете в свой код.

Итак, если у вас есть:

if (condition) then
{
   // do stuff
}
else
{
   // do other stuff
}

Как вы решаете, что лучше, так или так:

   if (!condition) then
   {
     // do other stuff
   {
   else
   {
     // do stuff
   }

Моя эвристика:

  1. Поддерживайте положительное состояние (меньше мысленный расчет при чтении)
  2. Поместите наиболее распространенный путь в первый блок

Я добавил тег «функция-выход», поскольку он связан с вопросом «несколько точек выхода - хороший или плохой».

finnw 03.10.2008 17:01

Я считаю этот вопрос очень важным. ИМХО, игнорирование этого вопроса - одна из главных причин пресловутого (и слишком вездесущего) «спагетти-кода» ...

Yarik 31.10.2008 12:59
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
12
2
1 039
23
Перейти к ответу Данный вопрос помечен как решенный

Ответы 23

Я предпочитаю первый. Условие должно быть как можно более простым и должно быть достаточно очевидным, что проще - вне условия и!

Я немного прояснил вопрос. condition и! condition не доставляют особых хлопот, но иногда они длиннее и сложнее с множественными «и» и «или».

WW. 03.10.2008 16:35

В этом случае инкапсулируйте условие в функцию. (См. Чистый кодекс Роберта Мартина, стр. 301)

Patrick McElhaney 03.10.2008 17:09
Ответ принят как подходящий

Я предпочитаю ставить наиболее распространенный путь на первое место, и я твердо верю в сокращение вложенности, поэтому я буду останавливаться, продолжать или возвращаться вместо того, чтобы продолжать, когда возможно. Обычно я предпочитаю проверять на соответствие положительным условиям или инвертировать [и называть] отрицательные условия на положительные.

if (condition)
    return;

DoSomething();

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

Обычно я стараюсь ограничиться одним оператором возврата для каждой функции. Преждевременный уход может вызвать у следующего парня всевозможное замешательство.

Kip 03.10.2008 16:28

множественные возвраты действительно являются проблемой только в том случае, если функция в любом случае слишком длинная. С короткой функцией дополнительные возвраты не должны вызывать путаницу, если парень не понимает код должным образом :)

workmad3 03.10.2008 16:32

Я полностью согласен с этим - только одна точка выхода на функцию. Это заставляет вас более четко структурировать код

Simon 03.10.2008 16:32

Множественные возвраты (и его преувеличение return null) - это современные формы goto ...

Andre Bossard 03.10.2008 16:34

Раньше я делал такие вещи, как объявление возвращаемой переменной в верхней части функции, а затем использовал логику if / else, поэтому последняя строка должна возвращать эту переменную, но кодовая база, в которую я вошел, не использовала этот подход. За короткое время я привык к нему и понял, что за ним очень легко следить.

cfeduke 03.10.2008 16:35

if (a> b) {if (b> c) {if (c> d) {/ * здесь много чего * /} else throw new ArgumentException ("c"); иначе выбросить новое исключение ArgumentException ("b"); иначе выбросить новое исключение ArgumentException "a"; Тьфу, иногда ОЧЕНЬ быстро стареет. Структура! = Читаемость.

Matthew Scharley 03.10.2008 16:36

goto лучше, чем команды throw / continue / break, потому что очевидно, куда передается управление. Вам не нужно анализировать фигурные скобки или делать предположения о вызывающем, чтобы понять, что делает goto.

Zooba 03.10.2008 17:01

Хм. Я восхищаюсь большей ясностью только с однократным возвратом, но в тех случаях, когда я сразу знаю, что мне нужно вернуть false из функции, кажется нечитаемым помещать сразу ложный возврат полностью внизу. Я, вероятно, мог бы это исключить; Я просто не уверен, что оно того стоит.

cori 03.10.2008 17:27

Я предпочитаю дежурство, если оно кончено, если еще ветки. +1

Amy B 03.10.2008 17:51

Это зависит от вашего потока. Для многих функций я буду использовать предварительные условия:

bool MyFunc(variable) {
    if (variable != something_i_want)
        return false;

    // a large block of code
    // ...
    return true;
}

Если мне нужно что-то делать в каждом случае, я буду использовать формат if (positive_clause) {} else {}.

Для меня это зависит от состояния, например:

if (!PreserveData.Checked)
{  resetfields();}

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

«Но офицер! Голоса сказали мне сделать это ...» Рад слышать, что я не единственный, кто это делает ...

Matthew Scharley 03.10.2008 16:37

Если код предназначен для проверки состояния ошибки, я предпочитаю сначала помещать этот код, а затем «успешный» код; концептуально это объединяет вызов функции и ее код проверки ошибок, что для меня имеет смысл, потому что они связаны. Например:

  if (!some_function_that_could_fail())
  {
     // Error handling code
  }
  else
  {
     // Success code
  }

Я согласен с Оли в использовании положительного условия if, когда это возможно.

Только, пожалуйста, никогда не делайте этого:

if (somePositiveCondition)
else {
    //stuff
}

Я часто видел это в одном месте, где работал, и задавался вопросом, не понимает ли один из кодеров, как не работает ...

Я предполагаю, что предложение if должно регистрировать ошибку, но кодировщик забыл это реализовать.

finnw 03.10.2008 17:04

Это происходило слишком много раз в коде, чтобы это могло быть случайностью.

Dana 03.10.2008 17:41

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

  1. ставь общий путь на первое место
  2. превратить отрицательный контроль в положительный (!full == empty)

Связанный с этим вопрос - называть логические переменные в отрицательном смысле. Ненавижу это: если (! NoData), то

WW. 03.10.2008 16:37

Я знаю, что это не совсем то, что вы ищете, но ... Многие разработчики используют «защитную оговорку», то есть отрицательный оператор «если», который прерывает работу метода как можно скорее. В этот момент на самом деле нет никакого «другого».

Пример:

if (blah == false)
{
    return; // perhaps with a message
}

// do rest of code here...

Есть некоторые хардкорные ребята из c / C++ / сборки, которые скажут вам, что вы разрушаете свой процессор !!! (во многих случаях процессоры отдают предпочтение «истинному» утверждению и пытаются «предварительно выбрать» следующее, что нужно сделать ... поэтому теоретически любое «ложное» условие очистит канал и будет выполняться на микросекунды медленнее).

На мой взгляд, мы находимся в точке, где «лучший» (более понятный) код побеждает за микросекунды процессорного времени.

ЦП не «отдают предпочтение« истинному »утверждению», они используют предсказание ветвлений и в некоторых случаях выполняют обе ветви спекулятивно.

Mr. Shiny and New 安宇 03.10.2008 17:23

Я думаю, что для одной переменной оператор not достаточно прост, и вопросы именования становятся более актуальными.

Никогда не называйте переменную not_X, если нужно, воспользуйтесь тезаурусом и найдите противоположное. Я видел много ужасного кода вроде

if (not_dead) {
} else {
}

вместо очевидного

if (alive) {
} else {
}

Затем вы можете разумно использовать (очень читаемый, нет необходимости инвертировать блоки кода)

if (!alive) {
} else {
}

Если мы говорим о большем количестве переменных, я думаю, что лучшим правилом является упрощение условия. Через некоторое время в проектах появляются такие условия, как:

if (dead || (!dead && sleeping)) {
} else {
}

Что переводится как

if (dead || sleeping) {
} else {
}

Всегда обращайте внимание на то, как выглядят условия и как их упростить.

Если у вас должно быть несколько точек выхода, укажите их первыми и проясните:

if TerminatingCondition1 then
  Exit
if TerminatingCondition2 then
  Exit

Теперь мы можем продвигаться к обычным вещам:

if NormalThing then
  DoNormalThing
else
  DoAbnormalThing

Две (противоречивые) цитаты из учебников:

Put the shortest clause of an if/else on top

- Аллен Голуб, «Достаточно веревки, чтобы выстрелить себе в ногу», стр. 52

Put the normal case after the if rather than after the else

- Стив МакКоннелл, «Полный код, 2-е изд.», Стр. 356.

Точка Голуб становится спорным, если ни один пункт очень долго. И этого легко добиться. Просто извлеките код в новую функцию.

Patrick McElhaney 03.10.2008 17:07

Программное обеспечение - это сбор знаний. Вы кодируете чьи-то знания о том, как что-то делать.

Программное обеспечение должно соответствовать тому, что «естественно» для проблемы. Если сомневаетесь, спросите кого-нибудь еще и посмотрите, что люди на самом деле говорят и делают.

А как насчет ситуации, когда "обычным" случаем является бездействие? Что тогда

if ( common ) {
    // pass
}
else {
    // great big block of exception-handling folderol
}

Или ты это делаешь?

if ( ! common ) {
    // great big block of except-handling folderol
}

Правило «всегда позитивно» - это не совсем то, что вам нужно в первую очередь. Вы хотите взглянуть на правила, более похожие на следующие.

  1. Всегда естественно - он должен читаться как английский (или любой другой язык, распространенный в вашей организации).
  2. По возможности, сначала общие случаи - чтобы они казались обычными.
  3. По возможности используйте позитивную логику; отрицательная логика может использоваться там, где это обычно говорится, или когда обычным случаем является бездействие.

Если один из двух путей очень короткий (от 1 до 10 строк или около того), а другой намного длиннее, я следую Здесь упоминается правило Голуба и помещаю более короткий фрагмент кода в if. Это упрощает просмотр сценария if / else на одном экране при просмотре кода.

Если это невозможно, я структурирую условие, чтобы сделать условие как можно более простым.

Когда я смотрю на проверку данных, я стараюсь внести свои условия в «белый список», то есть я проверяю, что я приму:

if DataIsGood() then
   DoMyNormalStuff
else
   TakeEvasiveAction

Скорее, чем наоборот, которая имеет тенденцию вырождаться в:

if SomeErrorTest then
  TakeSomeEvasiveAction
else if SomeOtherErrorCondition then
  CorrectMoreStupidUserProblems
else if YetAnotherErrorThatNoOneThoughtOf then
  DoMoreErrorHandling
else
  DoMyNormalStuff

Обычно вы можете сделать условие положительным, не переключая блоки if / else.

Изменять

   if (!widget.enabled()) {
     // more common 
   } else {
     // less common 
   }

к

   if (widget.disabled()) {
     // more common 
   } else {
     // less common
   }

Прогнозирование ветвления Intel Pentium предварительно выбирает инструкции для случая «если». Если вместо этого он следует за ветвью «else»: он сбрасывает конвейер команд, вызывая остановку.

Если вас очень волнует производительность: укажите наиболее вероятный результат в предложении if.

Лично я пишу это как

if (expected)
{
   //expected path
}
else
{
   //fallback other odd case
} 

Я также склонен следовать этому практическому правилу.

Christopher Lightfoot 31.10.2008 13:04

Я всегда ставлю на первое место наиболее вероятное.

В Perl для этого у меня есть дополнительная структура управления. Обратное к if.

unless (alive) {
     go_to_heaven;
} else {
     say "MEDIC";
}

Если у вас есть как истинные, так и ложные условия, я бы выбрал положительное условное - это уменьшает путаницу и в целом, я считаю, упрощает чтение вашего кода.

С другой стороны, если вы используете такой язык, как Perl, и особенно если ваше ложное условие является либо условием ошибки, либо наиболее распространенным условием, вы можете использовать структуру 'if', которая выполняет блок кода, если условие верно (т.е. противоположно if):

unless ($foo) {
    $bar;
}

Вы всегда должны ставить на первое место наиболее вероятный случай. Помимо того, что он более удобочитаемый, он быстрее. Это также относится к операторам switch.

Я ужасен, когда дело доходит до того, как я настраиваю операторы if. По сути, я настраивал его исходя из того, что именно я ищу, что приводит к тому, что все меняется.
если (userinput = null) {
взорваться насильственно ();
} else {
на самом деле что-то делать;
}

или, возможно, что-то вроде

если (1 + 1 = 2) {
делать вещи;
} else {
взорваться насильственно ();
}

Какая часть оператора if / else действительно что-то делает для меня, - это моя плохая привычка.

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

Итак, предположим, что должно быть предложение «else».

Я думаю, что читабельность / понятность накладывает как минимум три ключевых требования или правила, которые, к сожалению, часто конкурируют друг с другом:

  1. Чем короче первый блок (блок «if»), тем легче понять всю конструкцию «if-else». Когда блок «if» достаточно длинный, становится слишком легко упустить из виду наличие блока «else».

  2. Когда пути «if» и «else» логически асимметричны (например, «нормальная обработка» или «обработка ошибок»), в конструкции «if-else» автономный на самом деле не имеет большого значения, какой путь является первым, а какой вторым. . Однако, когда есть конструкции «if-else» несколько в непосредственной близости друг от друга (включая вложение), и когда все эти конструкции «if-else» имеют асимметрию одного и того же типа - именно тогда очень важно организовать эти асимметричные пути. последовательно.

    Опять же, это может быть «если ... нормальный путь ... еще ... ненормальный путь» для всех или «если ... ненормальный путь ... еще ... нормальный путь» для всех, но не быть смесью этих двух вариантов.

    При всех прочих равных условиях ставить нормальный путь на первое место для большинства людей, вероятно, более естественно (я думаю, что это больше связано с психологией, чем с эстетикой :-).

  3. Выражение, которое начинается с отрицания, обычно менее читаемо / понятно, чем выражение, которое этого не делает.

Итак, у нас есть эти три конкурирующих требования / правила, и реальный вопрос: какие из них важнее других. Для Аллена Голуба правило №1, вероятно, является самым важным. Для Стива МакКоннелла - это правило №2. Но я не думаю, что вы действительно можете выбрать только одно из этих правил в качестве единого правила.

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

Мои причины просты:

  • Правило №1 безоговорочно, и его невозможно обойти. Если один из блоков настолько длинный, что выходит за пределы экрана - он должен стать блоком «else». (Нет, не рекомендуется создавать функцию / метод механически, просто чтобы уменьшить количество строк в блоке «if» или «else»! Я предполагаю, что в каждом блоке уже есть минимальное количество строк логически оправданный.)

  • Правило №2 включает в себя множество условий: несколько конструкций «if-else», все из которых имеют одинаковую асимметрию и т. д. Так что во многих случаях оно просто неприменимо.

    Кроме того, я часто наблюдаю следующий интересный феномен: когда правило №2 действительно применяется и когда оно используется правильно, оно фактически не противоречит правилу №1! Например, всякий раз, когда у меня есть набор операторов «if-else» с асимметрией «нормальный или ненормальный», все «ненормальные» пути короче, чем «нормальные» (или наоборот). Я не могу объяснить этот феномен, но думаю, что это просто признак хорошей организации кода. Другими словами, всякий раз, когда я вижу ситуацию, когда правила №1 и №2 противоречат друг другу, я начинаю искать «запахи кода» и чаще всего их обнаруживаю; а после рефакторинга - тада! нет более болезненного выбора между правилом №1 и правилом №2, :-)

  • Наконец, правило № 3 имеет наименьшую область действия и, следовательно, является наименее критичным.

    Кроме того, как здесь отмечают другие коллеги, это правило часто очень легко «обмануть» (например, написать «if (disabled) ,,,» вместо «if (! Enabled) ...»).

Надеюсь, кто-нибудь сможет разобраться в этом опусе ...

Я обычно ставлю положительный результат (то есть метод) в начало, так что:

if (condition)
{
doSomething();
}
else
{
System.out.println("condition not true")
}

Но если для используемого метода условие должно быть ложным, я бы сделал следующее:

if (!condition)
{
doSomething();
}
else
{
System.out.println("condition true");
}

Другие вопросы по теме