Вы рассматриваете эту технику “ПЛОХО”?

21
задан Jon Seigel 4 April 2010 в 05:23
поделиться

28 ответов

Плохая практика, это зависит.

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

существует несколько альтернатив этому коду, который может или не может быть лучше, в зависимости от ситуации.

Ваш/в то время как решение

Ваше решение интересно, если Вы имеете много кода, но оцените "выход" этой обработки в некоторых ограниченных точках:

do
{
   bool isError = false ;

   /* some code, perhaps setting isError to true */
   if(isError) break ;
   /* some code, perhaps setting isError to true */
   if(isError) break ;
   /* some code, perhaps setting isError to true */
}
while(false) ;

// some other code   

проблема состоит в том, что Вы не можете легко использовать Ваш, "если (isError) повреждаются"; цикл, потому что он только выйдет из внутреннего цикла, не Вашего/в то время как блок.

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

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

Вызов goto... goto

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

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

необходимо все еще открыть блок, и вместо повреждения, использовать goto.

{
   // etc.
   if(/*some failure condition*/) goto MY_EXIT ;
   // etc.

   while(/* etc.*/)
   {
      // etc.
      for(/* etc.*/)
      {
         // etc.
         if(/*some failure condition*/) goto MY_EXIT ;
         // etc.
      }
      // etc.
      if(/*some failure condition*/) goto MY_EXIT ;
      // etc.
   }

   // etc.
}

MY_EXIT:

// some other code   

Таким образом, поскольку Вы выходите из блока через goto, нет никакого способа для Вас обойти некоторого конструктора Object с goto (который запрещается C++).

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

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

попытка/выгода

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

try
{
   // All your code
   // You can throw the moment something fails
   // Note that you can call functions, use reccursion,
   // have multiple loops, etc. it won't change
   // anything: If you want to exit the process,
   // then throw a MyExitProcessException exception.

   if(/* etc. */)
   {
      // etc.
      while(/* etc.*/)
      {
         // etc.
         for(/* etc.*/)
         {
            // etc.
            if(/*some failure condition*/) throw MyExitProcessException() ;
            // etc.
         }
         // etc.

         callSomeFunction() ;
         // the function will throw if the condition is met
         // so no need to test a return code

         // etc.
      }
      // etc.
   }

   // etc.
}
catch(const MyExitProcessException & e)
{
   // To avoid catching other exceptions, you should
   // define a "MyExitProcessException" exception
}

// some other code

, Если некоторое условие в коде выше, или в некоторых функциях, вызванных кодом выше, не соблюдают, то выдают исключение.

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

Обсуждение

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

Позволяют нам принять это.

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

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

Никто не может отклонить Ваше решение, прославленный goto. Не будет goto-запутанного-кода, потому что/в то время как не позволит Вам сделать это, но это - все еще семантический goto. Это может быть причинами, некоторые могли найти этот код "плохо": Они чувствуют запах goto, не находя его ключевое слово ясно.

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

Заключение

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

Так, в порядке предпочтения:

  1. попытка/выгода Использования
  2. Использование goto
  3. Использования Ваш/цикл с условием продолжения
  4. ifs/nested ifs
Использования
47
ответ дан 29 November 2019 в 06:03
поделиться

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

-1
ответ дан 29 November 2019 в 06:03
поделиться

Я думаю, что люди не честны здесь.

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

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

-1
ответ дан 29 November 2019 в 06:03
поделиться

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

, Почему не просто используют

if(isGood)
{
...Execute more code
}

?

-1
ответ дан 29 November 2019 в 06:03
поделиться

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

0
ответ дан 29 November 2019 в 06:03
поделиться

При разделении кода между , если (! isGood) повреждение; в отдельные функции, можно закончить с десятками функций, содержащих просто нескольких строк, так, чтобы деятели не упростили что-либо. Я не мог использовать возврат , потому что я не готов оставить функцию, существует все еще материал, который я хочу сделать там. Я признаю, что, вероятно, должен просто согласиться отдельный если (isGood) {...} условие для каждой части кода, которую я хочу выполнить, но иногда который привел бы к БОЛЬШОМУ КОЛИЧЕСТВУ фигурных скобок. Но я предполагаю, что признаю, что людям действительно не нравится тот тип конструкции, таким образом, условия для всего вьются!
спасибо за Ваши ответы.

0
ответ дан 29 November 2019 в 06:03
поделиться

Я думаю, что нет ничего в основном неправильно с техникой. Я думаю, что удостоверился бы, что bool называют чем-то более описательным, чем isGood. Хотя, теперь, когда я смотрю на него, почему был бы он не работать для помещения всего кода, который в курсе в сингл если (! isGood) блок? Цикл только выполняется однажды.

0
ответ дан 29 November 2019 в 06:03
поделиться

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

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

1
ответ дан 29 November 2019 в 06:03
поделиться

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

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

if (condition1(...) && condition2(...) && condition3(...) && ... && conditionN(...)) {
    // code that ought to run after all conditions
};
// code that ought to run whether all conditions are met or not

Затем я думаю, что это было бы более "читаемым" и более идиоматичным. Таким образом, можно сделать функции как:

bool conditionN(...) {
    if (!real_condition) return false;
    // code that ought to run
    return true;
};

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

1
ответ дан 29 November 2019 в 06:03
поделиться

Что относительно функционального подхода?


        void method1()
        {
            ... some code
            if( condition )
                method2();
        }

        void method2()
        {
            ... some more code
            if( condition )
                method3();
        }

        void method3()
        {
            ... yet more code
            if( condition )
                method4();
        }
1
ответ дан 29 November 2019 в 06:03
поделиться

Если Ваш код делает что-то другое, чем простое значение конструкций на месте, это - хороший знак, которым Вы рисковали в "милую" территорию.

В этом случае у Вас есть "цикл", который будет только работать однажды. Любой читатель кода должен будет получше вглядеться для выяснения то, что продолжается.

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

1
ответ дан 29 November 2019 в 06:03
поделиться

Я сказал бы, что Вашим решением может быть правильное решение, но оно зависит. Paul Tomblin отправил ответ, который лучше (серия если трубы)..., если он может использоваться.

решение Paul не может использоваться, когда существуют дорогие объектные инициализации по пути через Ваш цикл. Если созданные объекты используются на более поздних шагах, в то время как (0) решение лучше.

Тем не менее переменное именование должно быть улучшено. Кроме того, почему повторное использование переменная "Escape" так? Вместо этого захватите каждое отдельное состояние ошибки явно и повредите цикл так, чтобы было очевидно, что вызывает повреждение в каждой точке.

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

Другие предположили, что это - трудное для понимания идиомы. Ну, сначала Вы могли поместить комментарий, как предложено наверху цикла. Во-вторых, сделайте - в то время как (0) нормальная и общая идиома в макросах, которые все программисты C должны сразу распознать, таким образом, я просто не покупаю это.

1
ответ дан 29 November 2019 в 06:03
поделиться

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

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

If (isError)
{
   //Do whatever you need to do for the error and
   return;
}
более чем
If (!isGood)
{
  //Do something
}

Так проверяют на то, на что Вы действительно хотите проверить и свести проверки исключения к минимуму. Вашей целью должен быть readibility по тому, чтобы быть хитрым. Думайте о плохой душе, которая оказывается перед необходимостью приезжать и поддерживать Ваш код.

Одна из первых вещей я работал над, 28 лет назад была программа Фортрана, которая всегда должна была проверять, было ли графическое доступное устройство. Кто-то принял главное решение назвать булевскую переменную для этого LNOGRAF, поэтому если бы было графическое устройство, доступное, то это было бы ложью. Я полагаю, что это было установлено этот путь из-за ложной эффективности, проверка на устройство возвратила нуль, если было графическое устройство. Всюду по коду должны были видеть все проверки, было ли графическое устройство доступно. Это было полно:

, Если (.NOT. LNOGRAF)

я не думаю, что был сингл:

, Если (LNOGRAF)

в программе. Это использовалось в программном обеспечении планирования миссии для и крылатых ракет B-52. Это определенно учило меня называть свои переменные для того, на что я действительно проверяю.

1
ответ дан 29 November 2019 в 06:03
поделиться

Что бы там ни было я использовал конструкцию в нескольких местах. Запуск его ясно документируется, хотя:

    /* This is a one-cycle loop that simplifies error handling */
    do
    {
        ...modestly complex code, including a nested loop...
    } while (0);

Это находится в C, а не C++ - таким образом, исключениями не является опция. Если бы я использовал C++, то я полагал бы, что серьезно исключения использования обрабатывают исключения. Повторная тестовая идиома, предложенная Jeremy, также разумна; я использовал это более часто. RAII помог бы мне значительно, также; к сожалению, C не поддерживает это легко. И использование большего количества функций помогло бы. Обработка повреждений из вложенного цикла сделана повторным тестом.

я не классифицировал бы его как большой стиль; я автоматически не категоризировал бы его как "ПЛОХО".

2
ответ дан 29 November 2019 в 06:03
поделиться

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

1
ответ дан 29 November 2019 в 06:03
поделиться

У меня нет проблемы с этим, пока код читаем.

2
ответ дан 29 November 2019 в 06:03
поделиться

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

2
ответ дан 29 November 2019 в 06:03
поделиться

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

1
ответ дан 29 November 2019 в 06:03
поделиться

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

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

1
ответ дан 29 November 2019 в 06:03
поделиться

Осуществить рефакторинг. Чистое решение будет состоять в том, чтобы в большинстве случаев разделить этот код на меньшую функцию помощника, из которой можно возвратиться вместо того, чтобы убегать из not-actually-a-loop.

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

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

2
ответ дан 29 November 2019 в 06:03
поделиться

В основном Вы просто описали goto. Я использую goto в C все время. Я не рассматриваю это плохо, если Вы не используете его для эмуляции цикла (никогда не делают это!). Мое типичное использование goto в C должно эмулировать исключения (C, не имеет никаких исключений):

// Code

if (bad_thing_happened) goto catch;

// More code

if (bad_thing_happened) goto catch;

// Even more code

finally:

// This code is executed in any case
// whether we have an exception or not,
// just like finally statement in other
// languages

return whatever;

catch:

// Code to handle bad error condition

// Make sure code tagged for finally
// is executed in any case
goto finally;

За исключением факта, которые ловят и наконец имеют противоположный порядок, мне не удается видеть, почему этот код должен быть ПЛОХИМ просто, потому что он использует goto, если реальный код попытки/выгоды/наконец работает точно как это и просто не использует goto. Это не имеет никакого смысла. И таким образом мне не удается видеть, почему Ваш код должен быть отмечен как ПЛОХО.

5
ответ дан 29 November 2019 в 06:03
поделиться

То, что Вы пытаетесь сделать, является нелокальным восстановлением после отказа. Это - то, для чего goto. Используйте его. (на самом деле это - то, что обработка исключений для - но если Вы не можете использовать это, 'goto', или 'setjmp/longjmp' являются следующей лучшей вещью).

Этот шаблон, if(succeeded(..)) шаблон, и 'goto очистка', все 3 семантически и структурно эквивалентны. Используйте, какой бы ни каждый наиболее распространен в Вашем проекте кода. В непротиворечивости существует много значения.

я предостерег бы против if(failed(..)) break; на одной точке в этом, Вы приводите к неожиданному результату, должен Вы пытаться вложить циклы:

do{
   bool isGood = true;
   .... some code
   if(!isGood)
       break;
   .... some more code
   for(....){
      if(!isGood)
          break; // <-- OOPS, this will exit the 'for' loop, which 
                 //  probably isn't what the author intended
      .... some more code
   }
} while(false);
..... some other code, which has to be executed.

Ни goto cleanup, ни if(succeeded(..)) имеют это удивление, таким образом, я поощрил бы использовать один из этих двух вместо этого.

5
ответ дан 29 November 2019 в 06:03
поделиться

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

Как насчет того, чтобы использовать более обычные функции языка как функции?

bool success = someSensibleFunctionName();

if(success)
{
   ...
}

someCommonCodeInAnotherFunction();
1
ответ дан 29 November 2019 в 06:03
поделиться
public bool Method1(){ ... }
public bool Method2(){ ... }

public void DoStuff(){
    bool everythingWorked = Method1() && Method2();
    ... stuff you want executed always ...
}

причина, почему это работает, происходит из-за чего-то названного логикой короткого замыкания. Method2 не назовут, если Method1 возвратит false.

Это также обладает дополнительным преимуществом, что можно повредить метод в меньшие блоки, которые будут легче к модульному тесту.

8
ответ дан 29 November 2019 в 06:03
поделиться

Вы в значительной степени просто маскируете "goto" как поддельный цикл. Нравится ли Вам gotos или нет, Вы так же, как далеко впереди использовали бы реальный явный goto.

Лично, я просто записал бы это как

bool isGood = true;

   .... some code

   if(isGood)
   {
   .... some more code
   }

   if(isGood)
   {
   .... some more code
   }
31
ответ дан 29 November 2019 в 06:03
поделиться

Вы усложнили нелинейный поток управления в трудном для распознавания идиомы. Так, да, я думаю, что эта техника плоха.

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

12
ответ дан 29 November 2019 в 06:03
поделиться

Это является замысловатым и сбивает с толку, я сразу фрагментировал бы его.

Рассматривают эту альтернативу:

private void DoSomething()
{
   // some code
   if (some condition)
   {
      return;
   }
   // some more code
   if (some other condition)
   {
      return;
   }
   // yet more code
}

Также рассматривают разбивание кода выше больше чем в один метод.

11
ответ дан 29 November 2019 в 06:03
поделиться

Почему использование поддельный цикл? Можно сделать то же самое с методом, и это, вероятно, не будут считать "плохой практикой", как это более ожидается.

someMethod()
{
   .... some code

   if(!isGood)
       return;

   .... some more code

   if(!isGood)
       return;

   .... some more code

 }
24
ответ дан 29 November 2019 в 06:03
поделиться
Другие вопросы по тегам:

Похожие вопросы: