Последовательные проверки успеха - PullRequest
2 голосов
/ 03 мая 2010

Большинство из вас, вероятно, столкнулись с ситуацией, когда несколько вещей должны быть проверены и в определенном порядке, прежде чем приложение сможет продолжить работу, например, в очень простом случае создания прослушивающего сокета (socket, bind, listen, accept так далее.). Есть по крайней мере два очевидных способа (не принимайте это 100% дословно):

if (1st_ok)
{
  if (2nd_ok)
  {
  ...

или

if (!1st_ok)
{
  return;
}

if (!2nd_ok)
{
  return;
}
...

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

Ответы [ 8 ]

4 голосов
/ 03 мая 2010

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

2 голосов
/ 03 мая 2010

Это зависит от языка - например, в C ++ вы вполне можете использовать исключения, в то время как в C вы можете использовать одну из нескольких стратегий:

  • if / else blocks
  • goto (один из немногих случаев, когда одна метка goto для обработки «исключений» может * оправдываться
  • использовать break в цикле do { ... } while (0)

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

1 голос
/ 03 мая 2010

А как же

if (1st_ok && 2nd_ok) { }

или если нужно выполнить какую-то работу, как в вашем примере с сокетами

if (1st_ok()  &&  2nd_ok()) { }
1 голос
/ 03 мая 2010

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

1 голос
/ 03 мая 2010

Я предпочитаю вторую версию.

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

1 голос
/ 03 мая 2010

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

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

// Checking for llama integration
if (!1st_ok)
{
  return;
}

// Llama found, loading spitting capacity
if (!2nd_ok)
{
  return;
}

// Etc.
0 голосов
/ 03 мая 2010

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

Лично я предпочитаю второй случай, который вы изложили. Мне легче следовать (и отлаживать) код. То есть по мере продвижения кода он становится «более правильным». По моему собственному опыту, этот метод кажется предпочтительным.

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

error = foo1 ();
if ((error == OK) && test1)) {
    error = foo2 ();
}
if ((error == OK) && (test2)) {
    error = foo3 ();
}
...
return (error);

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

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

Надеюсь, это поможет.

0 голосов
/ 03 мая 2010

Я избегаю первого решения из-за вложенности.

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

Конечно, правила кодирования также запрещают goto.

Мой обходной путь - использовать локальную переменную:

bool isFailed = false; // or whatever is available for bool/true/false

if (!check1) {
    log_error();
    try_recovery_action();
    isFailed = true;
}

if (!isfailed) {
    if (!check2) {
        log_error();
        try_recovery_action();
        isFailed = true;
    }
}
...

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...