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


169

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

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

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

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

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

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

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

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

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



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

11
Что говорится в руководстве по стилю вашей компании?
Торбьерн Равн Андерсен

4
Частичный ответ на ваш «Второй» вопрос. Если один из блоков действия является длинным, а другой - коротким, протестируйте условие, которое ставит короткий блок первым, чтобы было меньше шансов пропустить его при чтении кода. Это условие может быть отрицанием или переименовано, чтобы сделать его проверкой на истинность.
Итан Болкер

9
Решарперу есть, что сказать другу, в духе «Ваш код избыточен и бесполезен. Вы хотите, чтобы я удалил / реорганизовал его?»
BgrWorker

Ответы:


184

Явный elseблок

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

Я даже не упоминаю тот факт, что за ifутверждением не обязательно следует либо «за», elseлибо «ничего»: за ним может следовать один или несколько elifсимволов «s».

Наличие 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 если Zero) на if (foo)to JNZ(Jump если не Zero) для if (!foo).
8bittree

71
« создавать дополнительные свойства с четкими именами »: если у вас нет более глубокого анализа домена, это может привести к изменению глобальной области (исходного класса) для решения локальной проблемы (применения определенного бизнес-правила). Здесь можно создать локальные логические значения с желаемым состоянием, такие как «var isSlave =! This.IsMaster», и применить свой if к локальным логическим значениям вместо создания нескольких других свойств. Итак, посоветуйтесь с долей соли и потратьте некоторое время на анализ домена, прежде чем создавать новые свойства.
Мачадо

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
Я чувствую, что не многие люди согласятся с тем, как следует обрабатывать if-else с возвратами, и некоторые могут даже сказать, что это не должно обрабатываться одинаково в каждом сценарии. У меня нет сильных предпочтений, но я определенно вижу аргумент в пользу многих других способов сделать это.
Dukeling

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; }аналогично, пустое else не требуется в случае 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условия, заключается в том, что код должен быть ясным и очевидным. Пустое предложение then (например, if (condition) ; else do_something;) нарушает это. Это запутанное программирование, заставляющее даже самых опытных программистов выполнять резервное копирование и перечитывать ifусловие в его отрицательной форме. Поэтому сначала запишите его в форме с отрицанием и пропустите инструкцию else (или оставьте пустое else или комментарий на этот счет, если это необходимо).



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


6
!также легко упускается из виду. Это слишком кратко.
Торбьерн Равн Андерсен

4
Нет, пустое другое не проясняет, что программист задумался над этим. Это приводит к еще большей путанице: «Были ли запланированы какие-то заявления, и они забыли или почему пустое предложение?» Комментарий намного понятнее.
Саймон

9
@ Симон: Типичная форма else { /* Intentionally empty */ }, или что-то в этом роде. Пустое остальное удовлетворяет статический анализатор, который бездумно ищет нарушения правил. Комментарий сообщает читателям, что пустое остальное является преднамеренным. Опять же, опытные программисты обычно пропускают комментарий как ненужный, но не пустой. Высоконадежное программирование - это отдельная область. Конструкции выглядят довольно странно для посторонних. Эти конструкции используются из-за уроков из школы тяжелых ударов, ударов, которые очень тяжелы, когда жизнь и смерть или $$$$$$$$$ под рукой.
Дэвид

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

2
@ ThorbjørnRavnAndersen Нет, notэто ключевое слово в C ++, как и другие побитовые и логические операторы. Смотрите альтернативные представления операторов .
Руслан

31

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

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. Мне вспоминается цитата Мартина Фаулера: «Любой может писать код, который могут понять компьютеры; хорошие программисты пишут код, который могут понять люди».
Тасос Папастилиану

2
@TasosPapastylianou Это обман. Противоположность «если проверка прошла успешно -> никаких действий не требуется» - «если проверка не прошла успешно -> необходимо действие». Вы дополнили его примерно 10 словами.
Джерри Б

@JerryB это зависит от "теста". Иногда отрицание является (лингвистическим) прямым, но иногда это не так, и может привести к путанице. В этих случаях яснее говорить о «отрицании / противоположности того, что результат истинен» против «результат не правдив»; однако дело в том, что было бы еще яснее ввести «пустой» шаг или инвертировать значение имен переменных. Это типично в ситуациях "де Морган": например if ((missing || corrupt) && !backup) -> skip -> else do stuff, гораздо яснее (+ другое подразумеваемое намерение), чемif ((!missing && !corrupt) || backup) -> do stuff
Тасос Папастилиану

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();что, как я лично чувствую, делает это еще яснее.
Baldrickk

23

При прочих равных предпочитают краткость.

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

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


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

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

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


7
// FALLTHRUТьфу, не в кричащих заглавных буквах или с сокращениями txtspk. В противном случае, я согласен и следую этой практике сам.
Коди Грей

7
@CodyGray - это единственное место, где кричать это хорошая идея. Большинство операторов switch заканчиваются в каждом случае break. Отсутствие этого breakпривело к некоторым впечатляющим сбоям программного обеспечения. Комментарий, который кричит читателям кода, что провал является преднамеренным, является хорошей вещью. То, что инструменты статического анализа могут искать этот конкретный паттерн и не жаловаться на провал, делает его еще лучше.
Дэвид

1
@Wossname: я только что проверил vim, и он не распознает никаких изменений FALLTHRU. И я думаю , что , по уважительной причине: TODOи FIXMEкомментарии комментарии , которые должны быть grepable , потому что вы хотите , чтобы иметь возможность задать свои , git grepкоторые известные дефекты у вас есть в вашем коде, и где они расположены. Падение не является недостатком, и нет никаких оснований для этого, как всегда.
Мастер

4
@DavidHammen Нет, кричать не очень хорошая идея: если у вас есть // падение вместо ожидаемого перерыва; этого должно быть достаточно, чтобы любой разработчик сразу понял. Кроме того, // падение не говорит о дефекте, оно просто сигнализирует о том, что обстоятельства были учтены, и что обрыв; который, кажется, отсутствует, действительно предназначен, чтобы не быть там. Это чисто информационная цель и кричать не о чем.
начальник

1
@cmaster - Ваше мнение - это нехорошо . Мнения других людей отличаются. И то, что ваша конфигурация vim не распознает FALLTHRU, не означает, что другие люди не настроили vim для этого.
Дэвид

6

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

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


2
Это ненужное вертикальное пространство является следствием необходимости всегда использовать фигурные скобки, запретить пропуск других элементов и использовать стиль Аллмана для отступов.
Дэвид

Ну, я думаю, что всегда использовать фигурные скобки - это хорошо, потому что это делает намерения разработчиков явными - нет места догадкам при чтении их кода. Это может звучать глупо, но, поверьте мне, особенно при наставничестве начинающих разработчиков, ошибки, связанные с отсутствующими скобками, действительно случаются. Лично я никогда не был поклонником отступов в стиле Аллмана, именно по той причине, о которой вы упоминаете: ненужное вертикальное пространство. Но это только мое мнение и личный стиль. Это то, чему я научился изначально, поэтому читать всегда было удобнее.
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;
            }
        }

Помните, что эти правила применяются только тогда, когда вы пишете новый код . ИМХО Пустые 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первый возврат принимается , но во многом это спорный вопрос.)
Дэвид Richerby

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

1

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

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

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

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

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


2
Я вижу, так что это было обычной практикой в ​​какой-то момент.
Пацуан

@ Пацуан - да, в некоторой степени. Хотя именно тогда ассемблер мэйнфреймов стал серьезной альтернативой для критичного к производительности кода.
кикстарт

1
«Давным-давно ... было принято, что отрицательные проверки были менее эффективными». Ну, они есть, если компилятор не оптимизируется if !A then B; else Cдо if A then C; else B. Вычисление отрицания условия является дополнительной инструкцией процессора. (Хотя, как вы говорите, это вряд ли будет значительно менее эффективным.)
Дэвид Ричерби

@DavidRicherby И если мы говорим в целом, некоторые языки могут сделать такие оптимизации очень сложно. Возьмите C ++ с перегруженными !и ==операторами, например. Не то чтобы кто-то действительно должен был это делать , заметьте ...
CVn

1

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

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

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


0

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


1
Я слышал, что вы никогда не работали на работодателя, который оценивает вашу работу на основе SLOC, произведенных за период ...
CVn

0

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

void foo()
{
    if (condition) return;

    doStuff();
    ...
}

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


0

При рассмотрении аргумента «всегда иметь предложение else» есть один момент, которого я не видел ни в одном другом ответе: он может иметь смысл в стиле функционального программирования. Вроде, как бы, что-то вроде.

В стиле функционального программирования вы имеете дело с выражениями, а не с утверждениями. Таким образом, каждый блок кода имеет возвращаемое значение, включая 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

... за оператором if следует оператор else, вне зависимости от того, есть что добавить в него или нет.

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

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

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

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

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

Хорошо продолжать задавать себе вопросы.

Это хорошая / плохая / "не имеет значения" практика?

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

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

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


0

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

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

Когда это заявлено

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

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

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

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

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

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

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

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


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

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

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

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

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


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

@ Санчизы Утверждения не противоречат, поскольку вы вывели их из контекста. Первые утверждения заключаются в том, что два противоположных двоичных измерения троичного состояния не могут быть заменены двоичным отрицанием. Второе утверждение заключается в том, что для правильной работы приложения может быть достаточно лишь частичного знания троичного состояния. Что касается дверей, то вопрос ОП касался открытых и закрытых дверей. Я просто указываю на очевидное предположение, которое может повлиять на то, как обрабатываются данные.
Питер М

-1

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

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

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

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

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.