2010-03-06 2 views
8

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

if(a <= 0) 
     return false; 
if(strlen(str) <= a) 
     return false; 
if(str[a] == NULL) 
     return false; 
return true; 

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

if(a >0) 
{ 
    if(strlen(str) > a) 
    { 
      if(str[a] != NULL) 
      { 
       return true; 
      } 
     } 
    } 

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

Какой, по вашему мнению, лучше использовать?

(код в этом примере только для демонстрации не берут его в буквальном смысле)

+1

Второй формат будет доступен для чтения. И вы можете иметь эти три выражения в условии &&. Итак, вы будете использовать только одно условие if. – Pavunkumar

+1

Возможно, мой образец кодирования не был «правильным» для вопроса. Я знаю, что вы можете поставить одно условие линии. Но я пытаюсь спросить о самой привычке кодирования. – aviv

ответ

2

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

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

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

Пример кода:

// function contract 
// 
// pre-conditions: 
// 
// o bar must not be zero 
// o foo_ptr must not be NULL 
// o foo_ptr must refer to a foo variant of type blue_foo 
// 
// ... 

Foo *foo_blue_init_with_bar(Foo *foo_ptr, int bar, foo_status *status) { 

    // ***** Test Pre-conditions ***** 

    // bar must not be zero 
    if (bar == 0) { 
     if (status != NULL) *status = FOO_STATUS_INVALID_BAR; 
     return NULL; 
    } 

    // foo_ptr must not be NULL 
    if (foo_ptr == NULL) { 
     if (status != NULL) *status = FOO_STATUS_INVALID_FOO_POINTER; 
     return NULL; 
    } 

    // foo_ptr must refer to a foo variant of type blue_foo 
    if (foo_ptr->type != blue_foo) { 
     if (status != NULL) *status = FOO_STATUS_INVALID_FOO_VARIANT; 
     return NULL; 
    } 

    // ***** Actual Work Goes Here ***** 

    ... 

} // end foo_blue_init_with_bar 

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

5

Лично я иду с первой секцией. Возвратитесь как можно раньше.

+0

Я бы предположил, что достойный компилятор будет оптимизировать второй случай, так что он в любом случае вернется рано. Нет? – amn

+0

кто сказал что-нибудь об оптимизации компилятора в этом ответе? – Anurag

0

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

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

+1

Почему это лучшая практика для 1 оператора возврата внутри функции? –

+1

Это аксиома _old_, что наличие только одного возврата в методе уменьшает сложность. В наши дни это не так часто, но все же обрушилось. –

8

Вопрос не совсем ясен, здесь. Если мы сравним:

if (c1) return false; 
if (c2) return false; 
if (c3) return false; 
return true; 

с этим:

if (!c1) { 
    if (!c2) { 
    if (!c3) { 
     return true; 
    } 
    } 
} 
return false; 

Тогда я голосовал бы за ни. Сделайте это вместо того, чтобы:

return !c1 && !c2 && !c3; 

Если речь идет о том или нет нескольких возвращения являются приемлемыми, то этот вопрос уже обсуждался (см Should a function have only one return statement)

2

Первый подход выглядит aestheticly лучше, но я не Не думайте, что это захватывает то, что вы пытаетесь сделать. Если все условия логически совпадают, тогда поместите их в один оператор if (я положил свои логические операторы в начале строки, чтобы упростить комментирование отдельных условий во время тестирования (зависание из больших предложений SQL WHERE). ) :

if(a > 0 
     && strlen(str) > a 
     && str[a] != NULL) 
    { 
     return true; 
    } 

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

return (a > 0 
     && strlen(str) > a 
     && str[a] != NULL); 
1

Если условия имеют аналогичные или связанные с семантикой, это, вероятно, лучше используйте логическое выражение вместо связки if s. Что-то вроде

return a > 0 && strlen(str) > a && str[a] != NULL; 

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

В большинстве случаев if s в коде являются «несбалансированными»: они либо имеют только одну ветку (нет else), либо одна из ветвей «тяжелая», а другая - «светлая». В таких случаях всегда лучше сначала иметь дело с «простой» ветвью, а затем явно указывать, что обработка для этой ветки завершена. Это достигается с помощью первого варианта из вашего вопроса: определить ситуацию, которая требует «простой» обработки, сделать это и сразу return (если вам нужно оставить функцию) или сделать continue (если вам нужно перейти к следующему итерация цикла). Именно это мгновенно return (или continue), которые помогают читателю понять, что эта ветка выполнена, и больше не нужно беспокоиться об этом. Таким образом, типичная функция будет выглядеть следующим образом: куча if s, которая «перехватывает» простые случаи, обрабатывает их (при необходимости) и return немедленно. И только после того, как все упрощенные случаи будут обработаны, начнется общая «тяжелая» обработка. Функцию, организованную таким образом, намного легче читать, чем ту, которая содержит кучу вложенных if s (как ваш второй вариант).

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

Чтобы возобновить вышеуказанное: Избегайте вложенных if s. Их трудно читать. Избегайте «несбалансированного» if s. Их также трудно читать. Предпочитайте «плоский» стиль разветвления, который сначала обрабатывает простые случаи и немедленно завершает обработку, делая явно return или continue.

0

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

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

isPositive = a > 0; 
isStringLonger = str.length() > a; 
isCharacterNonNull = str[a] != NULL; 

return isPositive && isStringLonger && isCharacterNonNull; 

Я придумал имена, основанные на вашем примере, поэтому они выглядят как мусор, но дело в том, что они не должны.

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

isValidString = isPositive && isStringLonger && isCharacterNonNull; 
return isValidString; 

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

return false if a <= 0 
return false if str.length() <= a 
return false if !str[a].nil? 
return true 
0

Ваши два образца делают разные вещи (см. Код ниже). Я бы предпочел второй, если она была написана как ниже (за исключением, может быть, с меньшим количеством скобок.

if((a>0) && (strlen(str) > a) && (str[a] != NULL)) 
{ 
    return true; 
} 

или написаны как

return ((a>0) && (strlen(str) > a) && (str[a] != NULL)) 

Чтобы ответить на ваш вопрос. Я предпочитаю первый (если приведенный выше код верен), и я всегда предпочитаю

  1. Меньше вкладок. Я предпочитаю все как можно больше, насколько это возможно, ваш второй пример (но не мой)
  2. Возврат наверху, если возможно, ex if (cond) return blah; else {code(); }
  3. имеют короткий код в верхней части и более кода в нижнем

    , если (условие) { код(); } else if (cond2) { код(); code(); } { код(); code(); code(); }

Смежные вопросы