Стиль кодирования - проверка ввода - PullRequest
2 голосов
/ 23 января 2009

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

class A;
void fun(A* p)
{
  if(! p)
  {
    return;
  }

 B* pB = p->getB();
  if(! pB)
  {
    return;
  }

.......

}

Или вы пишете так:

void fun(A* p)
{
  if(p)
  {
    B* pB = p->getB();
    if(pB)
    {
      .....
    }
  }
}

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

Ответы [ 7 ]

7 голосов
/ 23 января 2009

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

4 голосов
/ 23 января 2009

Многократный возврат: люди и стандарты кодирования идут в обе стороны. в C ++, imo, нет причин не отдавать предпочтение множественным возвратам (и исключениям), если вы используете RAII и т. д. Многие стандарты кодирования C предусматривают однократный ввод-однократный выход, чтобы гарантировать выполнение всего кода очистки, потому что нет RAII и уничтожение на основе области / очистка ресурса.

Должна ли функция иметь только один оператор возврата?

1 голос
/ 24 января 2009

Я склонен использовать этот тип проверок и возвратов в начале метода. Так что я как бы подражаю Eiffel как дизайн по контракту предварительные условия и утверждения , как описано в Бертран Мейер книга Конструкция объектно-ориентированного программного обеспечения, второе издание, Прентис Холл .

Для вашего примера вместо возврата void я бы вернул перечисление, идентифицирующее нарушение, например:

enum Violation { inviolate = 0, p_unspecified_unavailable, p_B_unspecified_unavailable,..... };

Violation fun(A* p)
{
//__Preconditions:___________________________________
  if (! p)         return p_unspecified_unavailable:
  if (! p->getB()) return pB_unspecified_unavailable;
//__Body:____________________________________________
   ....
//__Postconditions: _________________________________
   ..(if any)..
   return inviolate;
}

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

1 голос
/ 23 января 2009

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

class A;
void fun(A* p)
{
  ASSERT_NONZERO_RETURN_ON_FAIL( p );

  B* pB = p->getB();
  ASSERT_NONZERO_RETURN_ON_FAIL( pB );

  .......
}

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

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

1 голос
/ 23 января 2009

Я предпочитаю выполнять предварительную проверку ввода, но часто выполняю тесты одновременно

т.е.

if(!p || !p->getB()){
    return;
}

Если для работы функции требуется ввод, и язык ее поддерживает, я выбрасываю исключения Argument (см. .NET ArgumentNullException, ArgumentException), чтобы код не выполнял чистые возвраты в случае недопустимых состояний. Это позволяет вызывающей стороне знать, что аргументов недостаточно для завершения операции.

0 голосов
/ 23 января 2009

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


btnNext_OnClick(...)
{
if(!ValidateData())
 return;

// do things here
}


boolean ValidateData()
{
if(!condition)
{
 MessageBox.Show("Message", "Title", MessageBoxButtons.OK, MessageBoxIcons.Information);
 return false;
}

return true;
}

Таким образом, когда я добавляю поле, которое нуждается в проверке на уровне формы, я могу добавить еще одну проверку if в метод проверки.

0 голосов
/ 23 января 2009

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

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

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