Разработчик настаивает на том, что операторы не должны были отрицать условия и всегда должны иметь блок else

У меня есть знакомый, более опытный разработчик, чем я. Мы говорили о практике программирования, и я был ошеломлен его подходом к заявлениям «если». Он настаивает на некоторых практиках относительно высказываний, которые я нахожу довольно странными.

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

  if (condition)
{
    DoStuff ();
    возвращать что угодно;
}
еще
{
}
 

Во-вторых, , лучше проверить истинные значения, а не false. Это означает, что лучше проверить переменную «doorClosed» вместо переменной «! DoorOpened»

Его аргумент состоит в том, что он делает clearer то, что делает код.

Это меня немного смущает, поскольку сочетание этих двух правил может заставить его написать такой код, если он хочет что-то сделать, когда условие не выполняется.

  if (condition)
{
}
еще
{
    DoStuff ();
    возвращать что угодно;
}
 

Мое чувство об этом заключается в том, что он действительно очень уродлив и /или что улучшение качества, если оно есть, незначительно. Но, будучи младшим, я склонен сомневаться в своем инстинкте.

Итак, мои вопросы: Является ли это хорошей /плохой /«неважной» практикой? Это обычная практика?

158 голосов | спросил Patsuan 8 J0000006Europe/Moscow 2017, 16:35:19

15 ответов


176

Явный блок else

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

Я даже не упоминаю, что инструкция if не обязательно сопровождается либо else , либо ничего: за ней может следовать один или несколько < code> elif s.

Наличие инструкции return делает все хуже. Даже если у вас действительно был код для выполнения в блоке else , было бы более читаемым сделать это следующим образом:

  if (something)
{
    DoStuff ();
    возвращать что угодно;
}

doOtherThings ();
return somethingElse;
 

Это делает код менее двух строк и блокирует блок else . Оговорки охраны - все это.

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

  if (something)
{
}
если (другое)
{
}
еще
{
}
 

В предыдущем коде отсутствие безупречной строки прерывается после первого блока if , что очень легко неверно интерпретировать код. Однако, в то время как правило вашего коллеги затруднило бы неправильное использование кода, более простым решением было бы просто добавить новую строку.

Тест для true , а не для false

Второе правило может иметь некоторый смысл, но не в его текущей форме.

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

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

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

Условие выше может быть легко читаемым:

  if (this.IsSlave || (this.Synchronizing & amp; this.ReadOnly))
 
ответил Arseni Mourzenko 8 J0000006Europe/Moscow 2017, 16:46:29
62

Что касается первого правила, это пример бесполезной типизации. Мало того, что это займет больше времени, это вызовет огромное количество замешательства для всех, кто читает код. Если код не нужен, не записывайте его. Это может быть связано даже с отсутствием заполненного else в вашем случае, поскольку код возвращается из блока if :

  if (condition)
{
    DoStuff ();
    возвращать что угодно;
}

doSomethingElse (); //нет необходимости
вернуть что-то;
 

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

  bool doorNotOpen = false; //двойной негатив трудно рассуждать
bool doorClosed = false; //это намного яснее
 

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

  if (! condition)
{
    DoStuff ();
    возвращать что угодно;
}
 
ответил David Arno 8 J0000006Europe/Moscow 2017, 16:54:09
44

1. Аргумент в пользу пустых операторов else .

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

Единственное исключение - простой комментарий, что-то вроде строк /* Else не требуется * /. Это сигнализирует о том же намерении, что и три пустые строки. Еще одно исключение, где это пустое еще не нужно, - это то, где это явно очевидно для читателей кода и для автоматизированных инструментов анализа, которые пустые лишние. Например, if (condition) {do_stuff; вернуть; } Аналогично, пустое else не требуется в случае throw something или goto some_label 1 вместо возврат .

2. Аргумент для предпочтения if (condition) over if (! Condition) .

Это элемент человеческого фактора. Сложная логическая логика вызывает много людей. Даже опытный программист должен будет думать о if (! (Complex || (boolean & amp; & amp; condition)) do_that; else do_this; . Как минимум, перепишите это как if (complex || (boolean & amp; & amp; condition)) do_this; else do_that; .

3. Это не означает, что нужно отдавать предпочтение пустым , а затем операторам.

Второй раздел говорит «предпочитает», а не «ты будешь». Это скорее правило, чем правило. Причиной этого ориентира для предпочтения положительных if условий является то, что код должен быть ясным и очевидным. Предложение empty then (например, if (condition); else do_something; ) нарушает это. Это запутанное программирование, заставляющее даже самых опытных программистов выполнить резервное копирование и перечитать условие if в его отрицательной форме. Поэтому сначала напишите его в отрицательной форме и опустите оператор else (или укажите пустое или комментарий к этому эффекту, если это предусмотрено для этого).



1 Я написал, что тогда предложения, заканчивающиеся на return , throw или goto , не делают требуется пустое другое. Очевидно, что предложение else не требуется. Но как насчет goto ? В стороне, критически важные для безопасности правила программирования иногда запрещают раннее возвращение и почти всегда запрещают бросать исключения. Однако они допускают goto в ограниченной форме (например, goto cleanup1; ). Это ограниченное использование goto является предпочтительной практикой в ​​некоторых местах. Ядро Linux, например, является chockfull таких операторов goto .

ответил David Hammen 9 J0000006Europe/Moscow 2017, 13:55:16
29

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

  if (condition) {
    //Нет необходимости в действии, потому что ...
} else {
    do_else_action ()
}

если (условие) {
    do_if_action ()
} else {
    //Нет необходимости в действии, потому что ...
}
 

Но не:

  if (condition) {
    do_if_action ()
} else {
    //Мне сказали, что если всегда должно быть другое ...
}
 
ответил gnasher729 9 J0000006Europe/Moscow 2017, 22:06:15
23

Все остальные равны, предпочитают краткость.

То, что вы не пишете, никто не должен читать и понимать.

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


Итак, избегайте пустых ветвей, они не только бесполезны, но и необычны и, следовательно, приводят к путанице.

Кроме того, избегайте писать else-ветку, если вы выходите непосредственно из ветки if.

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

ответил Deduplicator 8 J0000006Europe/Moscow 2017, 21:35:32
6

Нет жесткого и быстрого правила относительно положительных или отрицательных условий для оператора IF, насколько мне известно. Я лично предпочитаю кодирование положительного случая, а не отрицательного, где это применимо. Я, конечно же, не буду этого делать, если это приведет меня к созданию пустого блока IF, а затем ELSE, полного логики. Если бы такая ситуация возникла, потребовалось бы 3 секунды, чтобы реорганизовать ее обратно на тестирование для положительного случая.

Что мне действительно не нравится в ваших примерах, это совершенно ненужное вертикальное пространство, занятое пустым ELSE. Для этого нет совершенно никакой причины. Он ничего не добавляет к логике, он не помогает документировать, что делает код, и он не повышает читаемость вообще. Фактически, я бы сказал, что добавленное вертикальное пространство может уменьшить .

ответил Langecrew 8 J0000006Europe/Moscow 2017, 16:50:06
5

Явный блок else

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

Оператор if , на мой взгляд, фактически охватывает две различные функции.

Если мы должны что-то сделать, сделайте это здесь.

Подобный материал явно не нуждается в части else .

  if (customer.hasCataracts ()) {
        назначениеSuggestions.add (новый CataractAppointment (клиент));
    }
    if (customer.isDiabetic ()) {
        customer.assignNurse (DiabeticNurses.pickBestFor (заказчик));
    }
 

и в некоторых случаях настаивать на добавлении else может ввести в заблуждение.

  if (k> n) {
        вернуть BigInteger.ZERO;
    }
    if (k <= 0 || k == n) {
        вернуть BigInteger.ONE;
    }
 

не совпадает с

  if (k> n) {
        вернуть BigInteger.ZERO;
    } else {
        if (k <= 0 || k == n) {
            вернуть BigInteger.ONE;
        }
    }
 

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

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

  //Количество выигрышей /потерь.
        если (двери [firstChoice] == Prize.Car) {
            //Мы победили бы без переключения!
            winWhenNotSwitched + = 1;
        } else {
            //Мы побеждаем, если перейдем к машине!
            если (двери [secondChoice] == Prize.Car) {
                //Мы выбрали правильно!
                winWhenSwitched + = 1;
            } else {
                //Плохой выбор.
                потеряно + = 1;
            }
        }
 

Помните, что эти правила применяются только при написании нового кода . IMHO Пустое предложение else должно быть удалено перед регистрацией.


Тест для true , а не для false

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

Даже если код типа

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

раздражает читателя, но делает его

  if (customer.canBuyAlcohol ()) {
        //Ничего не делать.
    } else {
        //...
    }
 

, по крайней мере, так плохо, если не хуже.

Я закодирован в BCPL много лет назад, и на этом языке есть предложение IF и UNLESS , чтобы вы могли кодировать гораздо больше читаем как:

 , если только (customer.canBuyAlcohol ()) {
        //...
    }
 

, что значительно лучше, но все же не идеально.


Мой личный процесс

Как правило, когда я пишу новый код, я часто добавляю пустой блок else в оператор if , чтобы напомнить мне, что я еще не рассмотрели эту возможность. Это помогает мне избежать ловушки DFS и гарантирует, что когда я просмотрю код, я замечаю, что есть что делать дальше. Тем не менее, я обычно добавляю комментарий TODO для отслеживания.

  if (returnVal == JFileChooser.APPROVE_OPTION) {
    handleFileChosen ();
  } else {
    //TODO: Обращайтесь к случаю, когда они нажали Отмена.
  }
 

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

ответил OldCurmudgeon 9 J0000006Europe/Moscow 2017, 12:31:57
1

В первом случае я использовал язык, который принудительно использовал IF-выражения таким образом (в Opal, язык зашивальника экрана мейнфрейма для установки интерфейса GUI на мэйнфреймовые системы) и только с одной строкой для IF и ELSE. Это был не приятный опыт!

Я бы ожидал, что любой компилятор оптимизирует такие дополнительные предложения ELSE. Но для живого кода он ничего не добавляет (в разработке он может быть полезным маркером для дальнейшего кода).

Время, когда я использую что-то подобное этим дополнительным предложениям, используется при обработке типов CASE /WHEN. Я всегда добавляю предложение по умолчанию, даже если оно пустое. Это долгосрочная привычка от языков, которые будут ошибочны, если такая статья не используется, и заставляет задуматься о том, действительно ли вещи действительно должны пройти.

Долгое время в мэйнфрейме (например, PL /1 и COBOL) было принято, что отрицательные проверки были менее эффективными. Это может объяснить 2-й пункт, хотя в наши дни имеется значительно более важная экономия эффективности, которая игнорируется как микрооптимизация.

Отрицательная логика, как правило, менее читаема, хотя и не столько на такой простой инструкции IF.

ответил Kickstart 9 J0000006Europe/Moscow 2017, 13:50:53
1

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

Вопрос об избежании негативов заслуживает еще большего внимания: обычно, когда вам нужно использовать логическое значение, вам нужно, чтобы некоторые блоки кода работали, когда они установлены, и некоторые другие блоки для работы, когда они не установлены. Таким образом, если вы применяете правило no-negatives, вы применяете либо команду if () {} else {...} (с пустым блоком if )! , или вы создаете второе логическое значение для каждого булева, содержащего его отрицательное значение. Оба варианта плохие, поскольку они путают ваших читателей.

Полезная политика заключается в следующем: никогда не используйте отрицательную форму в имени булина и выражайте отрицание как один ! . Оператор вроде if (! DoorLocked) совершенно ясен, оператор вроде if (! DoorUnlocked) завязывает мозги. Более поздний тип выражения - это то, что вам нужно избегать любой ценой, а не наличие одного ! в условии if () .

ответил cmaster 11 J0000006Europe/Moscow 2017, 04:26:14
0

Я бы сказал, что это определенно плохая практика. Добавление операторов else добавит целую пучку бессмысленных строк в ваш код, которые ничего не делают и еще труднее читать.

ответил ayrton clark 8 J0000006Europe/Moscow 2017, 16:49:15
0

Существует один момент при рассмотрении аргумента «всегда иметь аргумент else», который я не видел ни в одном другом ответе: он может иметь смысл в стиле функционального программирования. Сортировка.

В стиле функционального программирования вы используете выражения, а не выражения. Таким образом, каждый блок кода имеет возвращаемое значение, включая выражение if-then-else . Это, однако, исключает пустой блок else . Позвольте мне привести ваш пример:

  var even = if (n% 2 == 0) {
  вернуть «ровно»;
} else {
  return "odd";
}
 

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

  var even = (n% 2 == 0)? "даже странно";
 

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

ответил blalasaadri 11 J0000006Europe/Moscow 2017, 23:52:14
0
  

... инструкция if должна сопровождаться инструкцией else, есть ли что-то, чтобы в нее вставить или нет.

Я не согласен if (a) {} else {b (); } следует переписать как if (! a) {b (); } и , если (a) {b (); } else {} следует переписать как if (a) {b (); } .

Однако стоит отметить, что у меня редко есть пустая ветка. Это потому, что обычно я регистрирую, что я пошел в пустую ветку иначе. Таким образом, я могу полностью объяснить сообщения журнала. Я редко использую отладчик. В производственных средах вы не получаете отладчика; приятно иметь возможность устранять проблемы с производством с помощью тех же инструментов, которые вы используете для разработки.

  

Во-вторых, лучше проверить истинные значения, а не ложные. Это означает, что лучше проверить переменную «doorClosed» вместо переменной «! DoorOpened»

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

  

Я склонен сомневаться в своих инстинктах.

Хорошо продолжать задаваться вопросом.

  

Это хорошая /плохая /«неважная» практика?

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

  

Это обычная практика?

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

ответил Words Like Jared 12 J0000006Europe/Moscow 2017, 19:06:07
0

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

Однако это в некотором смысле расширенный комментарий, а не ответ.

Когда указано

  

Во-вторых, лучше проверить истинные значения, а не ложные. Что   означает, что лучше проверить переменную «doorClosed» вместо   '! doorOpened'

     

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

С моей точки зрения, здесь делается предположение, что состояние двери является двоичным состоянием, когда на самом деле это, по крайней мере, тернарное состояние:

  1. Дверь открыта.
  2. Дверь не полностью открыта и полностью закрыта.
  3. Дверь закрыта.

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

  1. Если датчик находится на открытой двери open : дверь открыта или не открыта.
  2. Если датчик находится на закрытой стороне двери: дверь закрыта или не закрыта.

И вы не можете обменивать эти понятия с помощью двоичного отрицания. Таким образом, doorClosed и ! DoorOpened скорее всего не являются синонимами, и любая попытка сделать вид, что они являются синонимами, является ошибочным мышлением, которое предполагает более полное знание состояния системы, чем фактически существует.

Возвращаясь к вашему вопросу, я бы поддержал формулировку, которая соответствует началу информации, которую представляет переменная. Таким образом, если информация получена из закрытой стороны двери, перейдите к doorClosed и т. Д. Это может означать использование либо doorClosed , либо ! DoorClosed как необходимых в различных заявлениях по мере необходимости, что приводит к потенциальной путанице с использованием отрицания. Но то, что он не делает, это неявно распространять предположения о состоянии системы.


В целях обсуждения моей работы объем информации, которую я имею в наличии о состоянии системы, зависит от функциональности, требуемой самой системой.

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

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

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

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

ответил Peter M 13 J0000006Europe/Moscow 2017, 20:31:03
-1

Еще один шаблон, который еще не упоминался, касается обработки второго случая с пустым if-блоком. Один из способов борьбы с ним - вернуться в блок if. Вот так:

  void foo ()
{
    если (условие) return;

    DoStuff ();
    ...
}
 

Это общий шаблон для проверки условий ошибки. Утвердительный случай все еще используется, и внутренняя часть его уже не пуста, и будущие программисты задаются вопросом, намеренно ли нет-op или нет. Он также может улучшить читаемость, поскольку вам может потребоваться создать новую функцию (таким образом, разбивая большие функции на более мелкие индивидуальные функции).

ответил Shaz 8 J0000006Europe/Moscow 2017, 23:06:04
-1

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

Тем не менее, есть много ненависти к пустым другим «он тратит строки» «лишняя набрав», это просто плохо. Вы можете сделать аргумент для перемещения скобок на одну и ту же строку, если вам действительно нужно вертикальное пространство, но если это проблема, вы не должны помещать ваш {на отдельной строке в любом случае.

Как упоминалось в другом месте, наличие блока else очень полезно, чтобы показать, что вы явно не хотите, чтобы в другом случае ничего не происходило. После выполнения большого количества функционального программирования (где еще требуется), я узнал, что вы всегда должны рассматривать другое, когда пишете if, хотя, как упоминает @OldCurmudgeon, существуют действительно два разных варианта использования. Нужно иметь другое, не следует. К сожалению, это не то, что вы всегда можете сказать с первого взгляда, не говоря уже о linter, поэтому догматика «всегда кладет блок».

Что касается «нет негативов», опять же, абсолютные правила плохие. Наличие пустого, если может быть странным, особенно если это тип, если это не нужно никому, поэтому пишите все это, а не! или == false - это плохо. Тем не менее, есть много случаев, когда негатив имеет смысл. Общим примером может быть кеширование значения:

  static var cached = null

func getCached () {
    if! cached {
        cached = (некоторые вычисления и т. д.)
    }

    return cached
}
 

, если фактическая (умственная /английская) логика включает в себя отрицательный результат, поэтому должен быть оператор if.

ответил zacaj 13 J0000006Europe/Moscow 2017, 16:23:36

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132