Подтвердить что ты не робот

Является ли "if (...) return...;" без "еще" считается хорошим стилем?

Этот код:

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();

Спасибо!

4b9b3361

Ответ 1

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

Я говорю глупости.

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

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

Rebuttal: То, что для 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, потому что вы будете создавать массивы или строки, в chich случае вы будете использовать vector, string или что-то подобное.

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

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

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

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

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

у вас получится:

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

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

3) Причина. Несколько точек возврата не являются объектно-ориентированными.   Rebuttal: Это действительно просто еще один способ сказать: "Все говорят, что многократные возвращения плохие, хотя я действительно не знаю почему".

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

Ответ 2

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

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

Ответ 3

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

return someCondition ? doSomething() : doSomethingElse();

Ответ 4

Это зависит, я предпочитаю то же самое, что FredOverflow

return someCondition ? doSomething() : doSomethingElse();

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

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

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

Ответ 5

Это зависит.

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

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

Приветствия и hth.,