Возвращает if (); без остального считается хорошим стилем?

Этот код:

if( someCondition )
    return doSomething();

return doSomethingElse();

по сравнению с этим кодом:

if( someCondition )
    return doSomething();
else
    return doSomethingElse();

По сути, они такие же, но какой стиль / исполнение / ... лучше всего (если, конечно, в ответе есть неискренняя часть)? Также рассмотрите случай с несколькими «if else»:

if( someCondition )
    return doSomething();
else if( someOtherCondition )
    return doSomethingDifferently();
//...
else
    return doSomethingElse();

Спасибо!


person rubenvb    schedule 21.11.2010    source источник
comment
У 2nd есть ненужный оператор else, никакое условие не проверяется, зачем его использовать?   -  person DumbCoder    schedule 21.11.2010
comment
@DumbCoder: Простая опечатка ... Я исправил.   -  person Ken Bloom    schedule 21.11.2010
comment
-1: Субъективно и вероятно начало пламенной войны.   -  person Ken Bloom    schedule 21.11.2010
comment
Dupe: stackoverflow.com/questions/3261849/   -  person BalusC    schedule 21.11.2010
comment
@BalusC: Боже мой, как я это пропустил: s   -  person rubenvb    schedule 21.11.2010


Ответы (5)


Когда у вас есть несколько операторов возврата в функции, это называется «ранним возвратом». Если вы выполняете поиск в Google для «досрочного возврата» вы найдете ссылку за ссылкой, в которой говорится, что это плохо.

Я говорю чушь.

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

1) Причина: раннее возвращение затрудняет уборку.

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

...

int foo()
{
  MyComplexDevice* my_device = new MyComplexDevice;
  // ...
  if( something_bad_hapened )
    return 0;
  // ...
  delete my_device;
  return 42;
}

ты делаешь это:

int foo()
{
  std::auto_ptr<MyComplexDevice> my_device(new MyComplexDevice);
  if( something_bad_hapened )
    return 0;
  // ...
  return 42;
} 

А досрочный возврат не вызовет утечки ресурсов. В большинстве случаев вам даже не нужно использовать auto_ptr, потому что вы будете создавать массивы или строки, в этом случае вы будете использовать vector, string или что-то подобное.

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

2) Причина: ранние возвраты делают код более сложным. Опровержение: ранний возврат действительно упрощает код.

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

Возьмем, к примеру, открытие базы данных. Это одна ответственность, но она состоит из множества шагов, каждый из которых может пойти не так. Откройте соединение. Авторизоваться. Получить объект подключения и вернуть его. 3 шага, каждый из которых может выйти из строя. Вы можете разбить это на 3 подэтапа, но тогда вместо такого кода:

int foo()
{ 
  DatabaseObject db = OpenDatabase(...);
}

у вас будет:

int foo()
{
  Connection conn = Connect(...);
  bool login = Login(...);
  DBObj db = GetDBObj(conn);
}

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

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

Иными словами, это на самом деле просто попытка втиснуть все в коробку в форме объекта, даже если это не место. Конечно, возможно, связь - это объект. Но разве логин? Попытка входа в систему не является (ИМО) объектом. Это операция. Или алгоритм. попытка взять этот алгоритм и втиснуть его в коробку в форме объекта - это беспричинная попытка ООП и приведет только к более сложному, сложному в поддержке и, возможно, еще менее эффективному коду.

person John Dibling    schedule 21.11.2010
comment
+10, хотя это позволяет мне дать только 1 - person Tergiver; 21.11.2010
comment
Хотя я уверен, что это можно рассматривать как личное и неоднозначное, я думаю, что одни только усилия (и, конечно же, тот факт, что я согласен с каждым пунктом) заслуживают зеленой галочки! - person rubenvb; 21.11.2010

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

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

person Puppy    schedule 21.11.2010

В зависимости от обстоятельств, я предпочитаю то же, что и FredOverflow

return someCondition ? doSomething() : doSomethingElse();

Если этого достаточно. Если это не так, я подумал о подобной ситуации - если у вас более длинный код - скажем, 30-40 строк или больше, должен ли я поставить return; на место, в этом нет необходимости. Например, рассмотрим следующую ситуацию:

if( cond1 )
{
    if( cond2 )
    {
         // do stuff
    }
    else if( cond3 )
    {
        // ..
    }
} 
else if( cond4 )
{
    // ..
}
else
{
    //..
}

Я задался вопросом - следует ли мне ставить return; (in void function) в конце каждого случая - это хороший или плохой стиль кодирования (поскольку не имеет значения, есть ли return; или нет). В конце концов, я решил поставить это, потому что разработчик, который позже прочитает этот код, должен знать, что это конечное состояние и в этой функции больше ничего не поделаешь. Не читать остальную часть кода, если он интересен только в этом случае.

person Kiril Kirov    schedule 21.11.2010

Это чисто вопрос личных предпочтений или стандартов кодирования. Лично я бы предпочел третий вариант:

return someCondition ? doSomething() : doSomethingElse();
person fredoverflow    schedule 21.11.2010
comment
Если два типа возвращаемых значений функции, которые могут быть преобразованы в тип возвращаемого значения охватывающей области, но не идентичны друг другу, тернарный оператор не сможет скомпилироваться. Тогда вам понадобится уродливый гипс, чтобы это исправить. Я бы предпочел тернарность здесь только тогда, когда задействованное условие тривиально и типы вряд ли изменятся (например, вызываемые функции находятся в стандартной библиотеке, а не в коде приложения). - person John Zwinck; 21.11.2010

По-разному.

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

Самая серьезная проблема с представленным кодом заключается в том, что вы не используете фигурные скобки. Это означает не совсем понимание того, что такое ясность. Если бы не это, я бы просто ответил на ваш главный вопрос «стремитесь к ясности», но сначала вам нужно понять ясность. В качестве первого шага на этом пути начните использовать фигурные скобки. Постарайтесь сделать ваш код доступным для чтения другими людьми, чтобы он мог быть понят с первого взгляда кем-то другим (или вами через несколько месяцев / лет).

Ура & hth.,

person Cheers and hth. - Alf    schedule 21.11.2010
comment
Ну, я считаю фигурные скобки для однострочных if-else непонятными и использую их только тогда, когда в условном блоке несколько строк, но это личное дело каждого и не имеет отношения к моему вопросу ... - person rubenvb; 21.11.2010
comment
-1: Ненавижу, когда люди жалуются на отсутствие фигурных скобок. Совершенно разумное соглашение - опускать фигурные скобки, если вы правильно делаете отступ в коде, и каждый знает, что нужно пойти и добавить фигурные скобки, если они добавят вторую строку кода. - person Ken Bloom; 21.11.2010
comment
Лично я нахожу добавление фигурных скобок без причины для УМЕНЬШЕНИЯ ясности, поскольку это добавляет визуальный шум и уменьшает объем функционального кода, который уместится на экране. - person swestrup; 21.11.2010
comment
@Ken: Да, кроме твоих коллег ... - person fredoverflow; 21.11.2010
comment
@Ken: люди находят самые удивительные вещи разумными. все зависит от контекста в голове. другие могут, однако, отреагировать. ура & hth. - person Cheers and hth. - Alf; 21.11.2010
comment
-1 за утверждение, что отсутствие фигурных скобок в вашем предпочтительном стиле свидетельствует о непонимании того, о чем идет речь - person Sebastian Negraszus; 21.11.2010
comment
@ Себастьян: это чрезвычайно важный показатель, как плотник, размахивающий молотком в голливудском стиле (известно, что этот парень не знает, о чем он). извини, но так оно и есть. ура и чт., - person Cheers and hth. - Alf; 21.11.2010
comment
Я всегда ставлю фигурные скобки, даже если линия всего одна. Предположим, что если вы хотите заменить вызов функции (или операцию) двумя другими операциями и сделать это, используя replace, чтобы заменить его везде в коде (такие вещи случаются), тогда возникнут проблемы, если нет фигурных скобок. Таким образом, использование replace вам не поможет, так как вам придется снова пройти через все случаи, добавить фигурные скобки и т. Д. Но да, это вопрос выбора, согласитесь. - person Kiril Kirov; 21.11.2010
comment
@Fred: При этом, в любом случае, я предпочитаю ваш способ написания выше, поэтому вопрос становится спорным для целей ответа на вопрос. - person Stuart Golodetz; 21.11.2010
comment
@Alf: Просто чтобы прояснить вашу точку зрения: будет ли стиль, в котором используются фигурные скобки, также показателем понимания значения ясности? Я видел много руководств по стилю, предлагающих разные стили фигурных скобок ... - person rubenvb; 21.11.2010
comment
Вот почему я считаю кудряшки злом. Сгруппировать статуэтки по отступам лучше, по-моему. - person Johannes Schaub - litb; 21.11.2010
comment
@ruben: Хороший вопрос! Я думаю, что классические фигурные скобки { и } слишком легко упустить из виду, потому что они всего лишь один символ, поэтому я предпочитаю триграфы ??< и ??> или определяю макросы begin и _6 _... просто шучу;) - person fredoverflow; 21.11.2010
comment
@rubenvb: ну, когда кто-то зацикливается на определенном стиле отступов и думает, что это серьезно, то от опыта человека зависит, является ли это признаком новизны или чего-то более серьезного. Легкая проверка на общую некомпетентность - это отсутствие у человека чувства юмора. около 2000/2001 было проведено несколько психологических исследований, которые пришли к выводу, что в целом некомпетентные люди редко обладают чувством юмора (однако они могут обойтись, запоминая ситуации, в которых смеются другие люди); проверить например википедия. ура и чт., - person Cheers and hth. - Alf; 21.11.2010
comment
@Johannes: ваше предпочтение без скобок может иметь смысл в академических кругах. перейти на Python. ;-) - person Cheers and hth. - Alf; 21.11.2010