Что положить в блок IF и что положить в блок ELSE?
Это второстепенный вопрос стиля, но каждый бит читабельности, который вы добавляете к своему коду, имеет значение.
Итак, если у вас есть:
if (condition) then
{
// do stuff
}
else
{
// do other stuff
}
Как вы решаете, лучше ли это или вот так:
if (!condition) then
{
// do other stuff
{
else
{
// do stuff
}
Мои эвристики:
- Держите условие положительным (меньше умственного вычисления, читая это)
- Поместите самый общий путь в первый блок
23 ответа
Я предпочитаю ставить наиболее распространенный путь на первое место, и я твердо верю в сокращение вложенности, поэтому я буду ломать, продолжать или возвращаться вместо того, чтобы пропустить, когда это возможно. Я обычно предпочитаю проверять положительные условия или инвертировать [и называть] отрицательные условия как положительные.
if (condition)
return;
DoSomething();
Я обнаружил, что благодаря резкому сокращению использования else мой код становится более читабельным и обслуживаемым, а когда мне приходится использовать else, он почти всегда является превосходным кандидатом для более структурированного оператора switch.
Две (противоречивые) цитаты из учебника:
Поместите самое короткое предложение из if/else сверху
- Аллен Голуб, "Достаточно веревки, чтобы выстрелить себе в ногу", стр. 52
Поставьте нормальный регистр после if, а не после else
- Стив Макконнелл, "Код завершен, 2-е изд.", Стр. 356
Я предпочитаю первый. Условие должно быть настолько простым, насколько это возможно, и оно должно быть достаточно очевидным, что проще из условия и! Условия
Если код предназначен для проверки состояния ошибки, я предпочитаю ставить этот код первым, а "успешный" код - вторым; концептуально это сохраняет вызов функции и код проверки ошибок вместе, что имеет смысл для меня, потому что они связаны между собой. Например:
if (!some_function_that_could_fail())
{
// Error handling code
}
else
{
// Success code
}
Это зависит от вашего потока. Для многих функций я буду использовать предварительные условия:
bool MyFunc(variable) {
if (variable != something_i_want)
return false;
// a large block of code
// ...
return true;
}
Если мне нужно что- то сделать в каждом случае, я буду использовать if (positive_clause) {} else {}
формат.
Когда я смотрю на проверку данных, я пытаюсь сделать мои условия "белым списком", то есть я проверяю, что я приму:
if DataIsGood() then
DoMyNormalStuff
else
TakeEvasiveAction
Вместо наоборот, который имеет тенденцию вырождаться в:
if SomeErrorTest then
TakeSomeEvasiveAction
else if SomeOtherErrorCondition then
CorrectMoreStupidUserProblems
else if YetAnotherErrorThatNoOneThoughtOf then
DoMoreErrorHandling
else
DoMyNormalStuff
Я согласен с Оли при использовании положительного предложения if, когда это возможно.
Просто, пожалуйста, никогда не делайте этого:
if (somePositiveCondition)
else {
//stuff
}
Я часто видел это в одном месте, где работал, и удивлялся, если один из программистов не понимает, как не работает...
Программное обеспечение - это захват знаний. Вы кодируете чьи-то знания о том, как что-то делать.
Программное обеспечение должно соответствовать тому, что "естественно" для проблемы. Если есть сомнения, спросите кого-то еще и посмотрите, что люди на самом деле говорят и делают.
А как насчет ситуации, когда "обычный" случай ничего не делает? Что тогда
if( common ) {
// pass
}
else {
// great big block of exception-handling folderol
}
Или ты это делаешь?
if( ! common ) {
// great big block of except-handling folderol
}
"Всегда позитивное" правило не совсем то, что вы хотите в первую очередь. Вы хотите посмотреть на правила больше как следующие.
- Всегда естественный - он должен читаться как английский (или любой другой язык в вашей организации).
- Там, где это возможно, сначала распространенные случаи - чтобы они казались общими.
- По возможности используйте позитивную логику; Негативную логику можно использовать там, где так обычно говорят, или в том случае, когда обычным делом является бездействие.
Я думаю, что для одной переменной оператор not достаточно прост, и вопросы именования становятся более актуальными.
Никогда не называйте переменную not_X, если нужно, используйте тезаурус и найдите противоположность. Я видел много ужасного кода, как
if (not_dead) {
} else {
}
вместо очевидного
if (alive) {
} else {
}
Тогда вы можете разумно использовать (очень читабельно, нет необходимости инвертировать блоки кода)
if (!alive) {
} else {
}
Если мы говорим о большем количестве переменных, я думаю, что лучшим правилом является упрощение условия. Через некоторое время проекты, как правило, получают условия, такие как:
if (dead || (!dead && sleeping)) {
} else {
}
Что переводится как
if (dead || sleeping) {
} else {
}
Всегда обращайте внимание на то, как выглядят условия и как их упростить.
Я знаю, что это не совсем то, что вы ищете, но... Многие разработчики используют "охранное предложение", то есть отрицательное выражение "если", которое выходит из метода как можно скорее. На данный момент, на самом деле нет "еще".
Пример:
if (blah == false)
{
return; // perhaps with a message
}
// do rest of code here...
Есть несколько хардкорных парней c/ C++/ Assembly, которые скажут вам, что вы разрушаете свой процессор!!! (во многих случаях процессоры предпочитают утверждение "истина" и пытаются "предварительно выбрать" следующее, что нужно сделать... поэтому теоретически любое "ложное" условие очистит канал и замедлит микросекунды).
На мой взгляд, мы находимся на том этапе, когда "лучший" (более понятный) код выигрывает за микросекунды процессорного времени.
Если один из двух путей очень короткий (от 1 до 10 строк или около того), а другой намного длиннее, я следую упомянутому здесь правилу Holub и помещаю более короткий фрагмент кода в if. Это облегчает просмотр потока if / else на одном экране при просмотре кода.
Если это невозможно, я структурирую, чтобы сделать условие как можно более простым.
Если у вас есть как истинные, так и ложные условия, я бы выбрал положительное условное условие - это уменьшает путаницу и, в общем, я считаю, что ваш код легче читать.
С другой стороны, если вы используете такой язык, как Perl, и, в частности, если ваше ложное условие является либо условием ошибки, либо наиболее распространенным условием, вы можете использовать структуру "never", которая выполняет блок кода, если только условие верно (то есть противоположно, если):
unless ($foo) {
$bar;
}
Прежде всего, давайте оставим в стороне ситуации, когда лучше вообще избегать использования "else" (я надеюсь, что все согласны с тем, что такие ситуации существуют, и определение таких случаев, вероятно, должно быть отдельной темой).
Итак, давайте предположим, что должно быть предложение "else".
Я думаю, что читаемость / понятность накладывает как минимум три ключевых требования или правила, которые, к сожалению, часто конкурируют друг с другом:
Чем короче первый блок (блок if), тем легче понять всю конструкцию if-else. Когда блок "если" достаточно длинный, становится слишком легко упускать из виду существование блока "еще".
Когда пути "if" и "else" логически асимметричны (например, "нормальная обработка" или "обработка ошибок"), в автономной конструкции "if-else" на самом деле не имеет большого значения, какой путь является первым, а какой - вторым, Тем не менее, когда есть несколько конструкций "if-else" в непосредственной близости друг от друга (включая вложение), и когда все эти конструкции "if-else" имеют асимметрию одного и того же типа - именно тогда очень важно организовать эти асимметричные пути последовательно.
Опять же, это может быть "если... нормальный путь... еще... ненормальный путь" для всех или "если... ненормальный путь... еще... нормальный путь" для всех, но это не должно быть смесью этих двух вариантов.
With all other conditions equal, putting the normal path first is probably more natural for most human beings (I think it's more about psychology than aesthetics:-).
An expression that starts with a negation usually is less readable/comprehensible than an expression that doesn't.
So, we have these three competing requirements/rules, and the real question is: which of them are more important than others. For Allen Holub the rule #1 is probably the most important one. For Steve McConnell - it is the rule #2. But I don't think that you can really choose only one of these rules as a single quideline.
I bet you've already guessed my personal priorities here (from the way I ordered the rules above:-).
My reasons are simple:
The rule #1 is unconditional and impossible to circumvent. If one of the blocks is so long that it runs off the screen - it must become the "else" block. (No, it is not a good idea to create a function/method mechanically just to decrease the number of lines in an "if" or "else" block! I am assuming that each block already has a logically justifiable minimum amount of lines.)
The rule #2 involves a lot of conditions: multiple "if-else" constructs, all having asymmetry of the same kind, etc. So it just does not apply in many cases.
Also, I often observe the following interesting phenomenon: when the rule #2 does apply and when it is used properly, it actually does not conflict with the rule #1! For example, whenever I have a bunch of "if-else" statements with "normal vs. abnormal" asymmetry, all the "abnormal" paths are shorter than "normal" ones (or vice versa). I cannot explain this phenomenon, but I think that it's just a sign of good code organization. In other words, whenever I see a situation when rules #1 and #2 are in conflict, I start looking for "code smells" and more often than not I do find some; and after refactoring - tada! no more painful choosing between rule #1 and rule #2,:-)
Finally, the rule #3 hase the smallest scope and therefore is the least critical.
Also, as mentined here by other colleagues, it is often very easy to "cheat" with this rule (for example, to write "if(disabled),,," instead of "if(!enabled)...").
I hope someone can make some sense of this opus...
Для меня это зависит от состояния, например:
if (!PreserveData.Checked)
{ resetfields();}
Я склонен говорить с самим собой с тем, что я хочу, чтобы логика была, и кодировать это к маленькому голосу в моей голове.
Предсказание ветви Intel Pentium предварительно выбирает инструкции для случая "если". Если вместо этого он следует ветке "else": он очищает конвейер инструкций, вызывая остановку.
Если вы сильно заботитесь о производительности: укажите наиболее вероятный результат в предложении "если".
Лично я пишу это как
if (expected)
{
//expected path
}
else
{
//fallback other odd case
}
Обычно вы можете сделать условие положительным, не переключаясь вокруг блоков if / else.
+ Изменить
if (!widget.enabled()) {
// more common
} else {
// less common
}
в
if (widget.disabled()) {
// more common
} else {
// less common
}
Я ужасен, когда дело доходит до того, как я настраиваю заявления. По сути, я настроил это на основе того, что именно ищу, что приводит к тому, что все будет по-другому.
if (userinput = null) {
explodeViolently ();
} еще {
на самом деле делать вещи;
}
или, может быть, что-то вроде
if (1+1=2) {
делать вещи;
} еще {
explodeViolently ();
}
Какой раздел инструкции if/else действительно делает для меня вещи, это моя плохая привычка.
Я вообще положил положительный результат (так метод) в начале так:
if(condition)
{
doSomething();
}
else
{
System.out.println("condition not true")
}
Но если условие должно быть ложным для используемого метода, я бы сделал это:
if(!condition)
{
doSomething();
}
else
{
System.out.println("condition true");
}
Вы всегда должны ставить наиболее вероятный случай первым. Помимо того, что он более читабелен, он быстрее. Это также относится к операторам switch.
- поставить общий путь первым
- превратить отрицательную проверку в положительную (
!full == empty
)
Я всегда держу наиболее вероятным первым.
В Perl у меня есть дополнительная структура контроля, чтобы помочь с этим. Обратное если.
unless (alive) {
go_to_heaven;
} else {
say "MEDIC";
}
Если у вас должно быть несколько точек выхода, сначала поставьте их и проясните:
if TerminatingCondition1 then
Exit
if TerminatingCondition2 then
Exit
Теперь мы можем прогрессировать с обычными вещами:
if NormalThing then
DoNormalThing
else
DoAbnormalThing
Как правило, если один значительно больше другого, я делаю больший if
блок.