Разница между GOTO и THROW? - PullRequest
6 голосов
/ 25 июля 2010

Многие люди недавно обвиняли меня в том, что я упомянул только одно слово - "goto".
Меня удивляет, почему это считается таким противным словом.
Мне известно о нескольких предыдущих обсуждениях этой темы,но это не убеждает меня - s ome ответов просто говорит "это плохо", даже не пытаясь объяснить , а некоторые приводят причины, не относящиеся к языкам сценариев, таким как PHP, IMO .

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

Кто-нибудь достаточно опытный, кто может сказать фундаментальную , концептуальную разницу между структурами кода двух следующих фрагментов кода?
Относительно этих самых фрагментов кода, а не "в теории" или "в общем "или" вот хороший комикс XKCD! ".
Почему первый считается блестящим, а второй - самым смертельным из грехов?

#$a=1;
#$b=2;

/* SNIPPET #1 */

try {
    if (!isset($a)) {
      throw new Exception('$a is not set');
    }
    if (!isset($b)) {
      throw new Exception('$b is not set');
    }
    echo '$a + $b = '.($a + $b)."<br>\n";
} catch (Exception $e) {
    echo 'Caught exception: ', $e->getMessage(), "<br>\n";
}

/* SNIPPET #2 */

if (!isset($a)) { 
  $message = '$a is not set';
  goto end;
}
if (!isset($b)) {
  $message = '$b is not set';
  goto end;
}
echo '$a + $b = '.($a + $b)."<br>\n";

end:
if (!empty($message)) {
  echo 'Caught exception: ', $message, "<br>\n";
}

Обратите внимание, что мне известен тот факт, что бросок более силен и гибок в приготовлении спагетти .Это не имеет принципиального значения для меня.Это вопрос использования, а не концепции.

РЕДАКТИРОВАТЬ
Многие люди говорили мне, что первый пример должен быть более новым.
Причина: следует использовать исключенияобрабатывать только ошибки, а не реализовывать бизнес-логику.
Это выглядит разумно.

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

Ответы [ 11 ]

13 голосов
/ 25 июля 2010

Throw / catch гораздо более гибки, чем goto: вы можете ловить функции, которые вызывают код, генерирующий исключение.Кроме того, требуемые деструкторы вызываются автоматически.

7 голосов
/ 25 июля 2010

Я думаю, вам нужно задать вопрос наоборот: зачем использовать goto, когда исключения идеально подходят для ситуации?Вот какие исключения являются для .

Вы также можете спросить «зачем использовать while, for и функции, когда я так же легко могу использовать goto

5 голосов
/ 25 июля 2010

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

Где исключения полезны , когда вы вызываете методы, которые могут иметь состояние ошибки, но могут 'Мы не можем справиться с этим сами, как показано в следующем псевдокоде:

try
{
    $c = add($a, $b);
    echo '$a + $b = '.($c)."<br>\n";
}
catch (Exception $e)
{
    echo 'Caught exception: ', $e->getMessage(), "<br>\n";
}

Метод add выполняет проверку ошибок:

if (!isset($a))
{
    throw new Exception('$a is not set');
}
if (!isset($b))
{
    throw new Exception('$b is not set');
}

затем возвращает результат сложения.

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

4 голосов
/ 25 июля 2010

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

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

Помните, что ваш код можно использовать повторно и гибко. goto - противоположность обоих ...

3 голосов
/ 21 октября 2011

Поскольку никто еще не дал ответ на этот вопрос, который ОП нашел приемлемым, я брошу свою шляпу в кольцо.

… фундаментальное, концептуальное различие между структурами кодадва следующих фрагмента кода?

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

Почему первый считается блестящим, а второй - самым смертоносным изгрехи?

Спасибо, полковник Шрапнель, за то, что открыл дверь для следующей напыщенной речи.

Каждая конструкция программирования вернапотенциал для злоупотребления.Догматическое преследование goto напоминает мне о подобном избиении PHP. PHP ведет к плохим практикам программирования. По сравнению с: Goto приводит к коду спагетти.

Проще говоря: goto - это «зло», потому что Эдсгер Дейкстра сказал так иНиклаус Вирт дал ему свою печать одобрения.; -p

Для вашего удовольствия от чтения: http://en.wikipedia.org/wiki/Goto

Вероятно, наиболее известной критикой GOTO является письмо Эдсгера Дейкстры 1968 года под названием Перейти к заявлению, которое считается вредным .В этом письме Дейкстра утверждал, что неограниченные операторы GOTO должны быть исключены из языков более высокого уровня, поскольку они усложняют задачу анализа и проверки правильности программ (особенно тех, которые включают циклы).Альтернативная точка зрения представлена ​​в структурированном программировании Дональда Кнута с помощью go to Statements, которая анализирует многие общие задачи программирования и обнаруживает, что в некоторых из них GOTO является оптимальной языковой конструкцией для использования.

Далее говорится,

Некоторые программисты, такие как разработчик ядра Linux и кодировщик Линус Торвальдс или инженер-программист и автор книг Стив Макконнелл, также возражают против точки зрения Дейкстры, заявляя, что GOTO могут быть полезной языковой функцией, улучшая программускорость, размер и четкость кода, но только при разумном использовании сравнительно разумным программистом.

Тем не менее, я лично не нашел практического применения GOTO со времен Commodore BASIC, но этони тут ни там.: -)

3 голосов
/ 14 октября 2010

goto полезен для таких вещей:

foreach ($items as $item) {
    if ($item->is_match()) goto match;
}

$item = new Item();

match:
render('find_item.php', $item);

в сопоставлении с этим:

$matching_item = null;
foreach ($items as $item) {
    if ($item->is_match()) {
        $matching_item = $item;
        break;
    }
}
if ($matching_item === null) {
    $item = new Item();
}

render('find_item.php', $item);
2 голосов
/ 21 апреля 2011

Дело в том, что «неправильно» в том, что goto может реализовывать многие виды механизмов управления, а не только throw, и поэтому имеет небольшую внутреннюю структуру.Из-за этого есть несколько причин, по которым программисту было бы интересно использовать альтернативы goto:

Во-первых, мы начинаем видеть переход скриптов из чисто интерпретируемого в скомпилированный байт-код.Хорошие примеры этого можно найти где угодно, например, Python, Perl 6, Scala (Lift), Clojure (Compojure).Даже в PHP есть компиляторы байт-кода.Поскольку goto имеет небольшую структуру, компиляция механизмов управления не может произойти, что является значительной потерей производительности, потому что современные компиляторы, как правило, очень хороши в этом.Для throw компиляторы могут начать предполагать, что генерируемое исключение не является распространенным, и, следовательно, оно оптимизирует «нормальный» путь управления часто за счет менее оптимального «ненормального» пути управления.

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

Кроме того, если ваш язык позволяет вам объявлять методы, которые throw исключают, компилятор / интерпретатор может сообщить вам, что вы не обработали все возможные исключения, с которыми могут сталкиваться вызываемые вами методы (аналогичноthrows Java, если у вас есть опыт там).С goto компилятор не знает, что вы пытаетесь сделать, поэтому он не поможет вам во время компиляции и тестирования.

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

if (!isset($a)) { 
  $message = '$a is not set';
  goto errorA;
}
if (!isset($b)) {
  $message = '$b is not set';
  goto errorB;
}
if (!moonInRightPhase) {
   goto errorP
}
echo '$a + $b = '.($a + $b)."<br>\n";

errorA:
// Do something non-trivial and unique to handle $a not being set.
goto afterError

errorP:
// Do something non-trivial and unique to handle moon phase
// This error requires drastic failure of code path:
goto failure

errorB:
// Do something non-trivial and unique to handle $b not being set.
goto afterError

afterError:
// Program continues

return SUCCESS

failure:
return FAILURE

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

Наконец, мой личный опыт заставляет меня не любить выражение goto просто потому, что его нелегко читать.Когда я пытаюсь понять фрагмент кода с четырьмя или пятью метками с именами some-error, fail, retry, и они не расположены рядом с кодом, нарушающим / исключающим, это может стать кошмаром для распутывания,И если его нелегко прочитать, то даже программист, пытающийся пересмотреть то, что он или она сделал год назад, может привести к ошибкам.

Как постскриптум к этому ответу, я не верю "неправильно""это правильный способ описать goto.Это могло произойти из сенсации знаменитой газеты Дейкстры.«Мощный» может быть лучшим термином, поскольку он подразумевает, что он может делать практически все, но даже самые простые ошибки делают «неправильное» превращением в «ужасно неправильное».Мы не должны запрещать инструменты, потому что они действительно опасны;мы должны лучше информировать людей об их опасности и позволить им решать.

2 голосов
/ 26 июля 2010

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

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

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

1 голос
/ 25 июля 2010

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

0 голосов
/ 26 июля 2010

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

Конечно, все зависит от того, как она используется.

...