Почему "else" редко используется после "if x then return"?

Этот метод:

boolean containsSmiley(String s) {
    if (s == null) {
        return false;
    }
    else {
        return s.contains(":)");
    }
}

может быть эквивалентно написано:

boolean containsSmiley(String s) {
    if (s == null) {
        return false;
    }

    return s.contains(":)");
}

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

(Связано: должна ли функция иметь только один оператор возврата?)

23 ответа

Решение

По моему опыту, это зависит от кода. Если я "защищаюсь" от чего-то, я сделаю:

if (inputVar.isBad()) {
    return;
}

doThings();

Суть ясна: если это утверждение ложно, я не хочу, чтобы функция продолжалась.

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

if (inputVar == thingOne) {
    doFirstThing();
} else if (inputVar == secondThing) {
    doSecondThing();
} else {
    doThirdThing();
}

Хотя это можно записать так:

if (inputVar == thingOne) {
    doFirstThing();
    return;
}
if (inputVar == thingTwo) {
    doSecondThing();
    return;
}
doThingThree();
return;

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

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

Это паттерн называется Guard Clause. Идея состоит в том, чтобы сделать все проверки заранее, чтобы уменьшить вложенные условия для повышения читабельности.

По ссылке:

double getPayAmount() {
    double result;
    if (_isDead) {
        result = deadAmount();
    } else {
        if (_isSeparated) {
            result = separatedAmount();
        } else {
            if (_isRetired) {
                result = retiredAmount();
            } else {
                result = normalPayAmount();
            }
        }
    }

    return result;
}

Используя пункт Guard, вы увидите этот результат:

double getPayAmount() {
    if (_isDead) return deadAmount();
    if (_isSeparated) return separatedAmount();
    if (_isRetired) return retiredAmount();

    return normalPayAmount();
};

Вы увидите это повсюду:

if (condition) {
    return var;
}
// by nature, when execution reaches this point, condition can only be false,
// therefore, the else is unnecessary
return other_var;

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

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

0x00: test [condition]
0x01: if result of test was not true, goto [0x04]
0x02: push [var] onto stack
0x03: goto [0x05]
0x04: push [other_var] onto stack
0x05: return from subroutine

Код (опять же, это псевдокод, а не сборка) будет действовать точно так же для if/then/else условна.

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

return (condition) ? var : other_var;

Это упрощает код и не создает никаких новых точек выхода.

Я предпочитаю писать это так:

boolean containsSmiley(String s) {
    return s != null && s.contains(":)");
}

Я бы предпочел первый вариант, так как он более читабелен.

В качестве аналогии сравните следующие 2 предложения: "Если сегодня идет дождь, тогда возьмите зонтик, а в противном случае возьмите солнечные очки". и "Если сегодня идет дождь, тогда возьми зонт, возьми солнечные очки". Первое предложение соответствует первому блоку кода вопроса, второе - второму. Первый гораздо понятнее и читабельнее, не так ли?

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

  1. Работает ли код должным образом во всех ситуациях. (Принцип наименьшего удивления)

  2. Может ли следующий разработчик (я или кто-то еще) понять, что он делает и почему.

  3. Насколько хрупок код в отношении изменений.

  4. Это просто, как это должно быть и не более. Т.е. не более или не в разработке.

Как только я счастлив, что выполнил все вышесказанное, остальное, как правило, просто падает в линию.

else избыточно Также некоторые IDE (Eclipse) и инструменты анализа (может быть, FindBugs) могут пометить это как предупреждение или ошибку, поэтому в этом случае программисты, скорее всего, удалят его.

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

boolean containsSmiley(String s) {
    assert s != null : "Quit passing null values, you moron.";
    return s.contains(":)");
}

Я переключился на общее правило: никогда. Когда-либо. передавать нулевые значения, если только внешние вызовы API не запрашивают об этом явно. Второе: если внешний метод может возвращать нулевые значения, замените его разумным ненулевым значением (например, пустой строкой) или добавьте аккуратную проверку. Меня тошнит от повторения if (thing == null) чеки.

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

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

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

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

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

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

if (s == null) {
    return false;
}
else if (s.Contains(":))")) {
    return true;
}
else if (s.Contains(":-(")) {
    return false;
}

return s.contains(":)");

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

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

... но это на самом деле не нужно, потому что мы все так хорошо комментируем наш код, верно?:)

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

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

Вторая форма, если проще / короче. Это не всегда значит яснее. Я предлагаю вам сделать то, что вы считаете наиболее ясным.. Лично я бы написал.

static boolean containsSmiley(String s) { 
    return s != null && s.contains(":)"); 
} 

Я бы предпочел одну точку выхода нескольким с точки зрения обслуживания. Конечный результат может быть изменен (или оформлен) в одной точке выхода, а не в n точках выхода.

Как видите, разные люди имеют разные мнения о читабельности. Некоторые люди думают, что меньшее количество строк кода делает код более читабельным. Другие считают, что симметрия второй формы делает ее более читаемой.

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

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

Ну, некоторые из причин просто конвенция, но есть одно преимущество в форме выше...

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

Потому что это то же самое, что написать одно из следующего, что дает доказательства намерения программиста:

boolean containsSmiley(String s) {
    if (s == null)   // The curly braces are not even necessary as the if contains only one instruction.
        return false;

    return s.contains(":)");
}

Или даже это:

boolean constainsSMiley(String s) {
    return string.IsNullOrEmpty(s) ? false : s.Contains(":)");
}

Эти две формы:

  1. Более элегантный;
  2. Легче читать;
  3. Leaner и Swifter для программиста чтения.
Другие вопросы по тегам