Как я должен обрабатывать ожидаемые ошибки? например. "Имя пользователя уже существует" - PullRequest
10 голосов
/ 12 мая 2010

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

Однако что мне делать, если я не могу обработать ошибку из текущей ссылки в цепочке? Например, у меня может быть класс, который используется для управления аутентификацией. Одним из методов может быть createUser($username, $password). Редактировать: Этот метод возвращает идентификатор пользователя или объект пользователя. В этой функции мне нужно определить, существует ли имя пользователя уже. Если это правда, как я должен предупредить вызывающий код об этом? Возврат null вместо пользовательского объекта является одним из способов. Но как тогда узнать, что вызвало ошибку?

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

Редактировать: Я забыл упомянуть: я использую PHP.


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

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

Во-вторых, в ответ на «исключения следует использовать только для исключительных ситуаций, и это не один» аргумент: «Когда я вызываю getFoo (), я фактически ожидаю получить Foo. Если Я не понимаю, это по определению исключительное событие. " (via, pkainulainen )

Ответы [ 7 ]

3 голосов
/ 12 мая 2010

Есть несколько общих шаблонов:

1. Бросить исключение .

2. Вернуть NULL или FALSE и установить переданную ссылку на ошибку. Э.Г.

function createUser($user, $password, &$error)
{
      //...
      // You could use any type of object here.  This is just a simple example.
      if(uniqueKeyFailure)
      {
          $error = new UserAlreadyExists();
          return NULL;
      }
      //..
}

Это можно назвать как:

$userCreateError = NULL;
$res = createUser($user, $pass, $userCreateError);
if($res === NULL)
{
  // do something with $userCreateError
}

3. Верните NULL или FALSE и предоставьте последнюю ошибку get (например, curl_error ).

Я бы порекомендовал 1 или 2. Основная причина, по которой люди избегают исключений из-за «неисключительных» ошибок, таких как пользовательский ввод, - это производительность. Об этом довольно много говорят, например: Производительность try-catch в php .

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

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

Я часто справляюсь с подобными вещами, прикрепляя свою проверку к какому-либо объекту домена (например, пользователю).

Например:

<?PHP
$user->name = 'Joe';
$user->password = 'secret';

//try to save the user.
if (! $user->save()){
   $errors = $user->getMessages();
   // ... do something with error messages
}else{
   //the user object set it's own ID.
   $id = $user->id;
}

Теперь много вещей отвлекается от пользователя. Там может быть целая вселенная объектов, но это зависит от вас.

Большая проблема здесь: почему в мире у вас есть класс аутентификации , создающий пользователя? Создание пользовательских объектов, вероятно, выходит за рамки аутентификации.

Может быть разумно иметь какой-нибудь метод, например authenticate($username,$password) , возвращающий объект типа 'user' (представляющий пользователя, который только что аутентифицировался), но даже это немного грязно.

Вместо этого рассмотрим что-то вроде:

<?PHP
$auth = new AuthService();
$u = new User();
$u->username='Joe';
$u->password='secret';

$authResult = $auth->authenticate($user);

if ($authResult->success){
  //$user has properties set as a side effect.
}else{
  //find out why it failed.
  $errors = $authResult->getErrors();
}

Конечно, для этого необходимо определить какой-то класс значения-значения для AuthService :: authenticate () для заполнения и возврата. Но, по крайней мере, вы не получите методы, которые иногда возвращают логические значения, а иногда возвращают объекты и т. Д.

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

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

например.

function numeric($input) {
    if (!is_numeric($input)) return 'Must be numeric.';
    return NULL;
}

function usernameavailable($input) {
    //query database....
    if ($result) return 'Username already taken, please choose another one.';
    return NULL;
}

Вы также можете использовать функции с параметрами:

function length($input, $min, $max) {
    if (strlen($input) < $min) return "Must be longer than $min characters.";
    if (strlen($input) > $max) return "Must be shorter than $max characters.";
    return NULL;
}

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

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

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

Пример из моей структуры:

$Form = new AjaxForm();
$Form->add(new TextBox('Username', 'usernameavailable|length[3,12]'));
$Form->add(new SubmitButton());

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

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

Если вы контролируете тип возвращаемого значения метода, вы также можете изменить его сигнатуру, чтобы она возвращала что-то вроде объекта OperationResult, который имел бы свойство ErrorDescription и свойство объекта UserID или User.

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

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

Есть ли у него стоимость? Да. Это достаточно большой, чтобы иметь значение? Скорее всего нет.

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

Это должно быть сделано следующим образом -

Создать объект ошибки с кодами ошибок в них, текстом ошибки и методом, вызвавшим ошибку.

Передать объект ошибки обратно.

Вы можете выбросить исключения, но исключения стоят дорого

РЕДАКТИРОВАТЬ - На основе редактирования ОП. В этом случае вам придется отбросить объект исключения обратно.

Создать класс исключений с кодом ошибки, описанием ошибки, именем класса, именем метода. Брось исключение обратно.

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

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

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