Несколько возвращаемых значений для обозначения успеха / неудачи. - PullRequest
5 голосов
/ 16 сентября 2008

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

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

В основном, техника подразумевает возвращение true для успеха и что-то , которое приравнивает к false для неудачи. Вот пример, чтобы показать, что я имею в виду:

define ('DUPLICATE_USERNAME', false);
define ('DATABASE_ERROR', 0);
define ('INSUFFICIENT_DETAILS', 0.0);
define ('OK', true);

function createUser($username) {
    // create the user and return the appropriate constant from the above
}

Прелесть этого в том, что в вашем вызывающем коде, если вам все равно, ПОЧЕМУ создание пользователя не удалось, вы можете написать простой и читаемый код:

if (createUser('fred')) {
    // yay, it worked!
} else {
    // aww, it didn't work.
}

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

$status = createUser('fred');
if ($status) {
    // yay, it worked!
} else if ($status === DUPLICATE_USERNAME) {
    // tell the user about it and get them to try again.
} else {
    // aww, it didn't work. log it and show a generic error message? whatever.
}

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

Недостатком является то, что у вас может быть только 7 "error" return values: false, 0, 0.0, "0", null, "", and (object) null. Если вы забудете использовать проверку личности, вы можете получить неверный ход выполнения вашей программы. Кто-то еще сказал мне, что использование констант, таких как enum, где все они равны ложному, "ick".


Итак, чтобы сформулировать вопрос: насколько приемлема такая практика? Вы бы порекомендовали другой способ добиться того же?

Ответы [ 14 ]

13 голосов
/ 16 сентября 2008

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

function createUser($username, &$error)

Тогда вы можете использовать:

if (createUser('fred', $error)) {
    echo 'success';
}
else {
    echo $error;
}

Внутри createUser просто заполните $ error любой ошибкой, с которой вы столкнетесь, и она будет доступна за пределами области действия функции из-за ссылки.

2 голосов
/ 16 сентября 2008

Насколько приемлема такая практика?

Я бы сказал, что это неприемлемо.

  1. Требуется оператор ===, что очень опасно. Если пользователь использовал ==, это приводит к очень трудной для поиска ошибке.
  2. Использование «0» и «» для обозначения false может измениться в будущих версиях PHP. Плюс во многих других языках "0" и "" не оцениваются как ложные, что приводит к большой путанице

Использование глобальной функции типа getLastError (), вероятно, является лучшей практикой в ​​PHP, поскольку она хорошо связана с языком , поскольку PHP по-прежнему в основном является процедурным языком. Я думаю, что другая проблема с подходом, который вы только что дали, состоит в том, что очень немногие другие системы работают так. Программист должен изучить этот способ проверки ошибок, который является источником ошибок. Лучше заставить все работать так, как ожидает большинство людей.

if ( makeClient() )
{ // happy scenario goes here }

else
{
    // error handling all goes inside this block
    switch ( getMakeClientError() )
    { case: // .. }
}
2 голосов
/ 16 сентября 2008

Часто вы возвращаете 0, чтобы указать успех, и 1, 2, 3 и т. Д., Чтобы указать различные сбои. Ваш способ сделать это отчасти хакерский, потому что вы можете иметь только столько ошибок, и этот вид кодирования рано или поздно укусит вас

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

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

2 голосов
/ 16 сентября 2008

Более распространенный подход, который я видел, когда исключения недоступны, - это хранить тип ошибки где-то в переменной 'last_error', а затем, когда происходит сбой (т.е. он возвращает false), искать ошибку.

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

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

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

2 голосов
/ 16 сентября 2008

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

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

1 голос
/ 17 сентября 2008

Изобретая колесо здесь. Использование квадратов.

ОК, у вас нет исключений в PHP 4. Добро пожаловать в 1982 году, взгляните на C.

У вас могут быть коды ошибок. Учитывая отрицательные значения, они кажутся более интуитивными, поэтому вам просто нужно проверить, если (createUser ()> 0).

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

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

Что происходит, когда у вас заканчиваются встроенные типы?

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

Является ли оператор PHP === достаточно быстрым, чтобы пройти через него? Это быстрее, чем коды ошибок? или любой другой метод?

Просто брось.

1 голос
/ 16 сентября 2008

Когда исключения недоступны, я бы использовал модель PEAR и предоставлял бы функциональность isError () во всех ваших классах.

0 голосов
/ 16 сентября 2008

Другие способы включают исключения:

throw new Validation_Exception_SQLDuplicate("There's someone else, hun");),

возвращающие структуры,

return new Result($status, $stuff);
if ($result->status == 0) {
    $stuff = $result->data;
}
else {
    die('Oh hell');
}

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

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

0 голосов
/ 16 сентября 2008

Мне нравится, как COM может обрабатывать звонки как с исключениями, так и без исключений. В приведенном ниже примере показано, как тестируется HRESULT и в случае сбоя выдается исключение. (обычно автоматически генерируется в tli-файлах)

inline _bstr_t IMyClass::GetName ( ) {
    BSTR _result;
    HRESULT _hr = get_name(&_result);
    if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
    return _bstr_t(_result, false);
}

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

0 голосов
/ 16 сентября 2008

Имеет смысл, что успешное выполнение возвращает true. Обработка общих ошибок будет намного проще:

if (!createUser($username)) {
// the dingo ate my user.
// deal with it.
}

Но вообще не имеет смысла связывать значение с разными типами ложного. Ложь должна означать одно и только одно, независимо от типа или того, как язык программирования относится к этому. Если вы все равно собираетесь определять константы состояния ошибки, лучше придерживайтесь switch / case

define(DUPLICATE_USERNAME, 4)
define(USERNAME_NOT_ALPHANUM, 8)

switch ($status) {
case DUPLICATE_USERNAME:
  // sorry hun, there's someone else
  break;
case USERNAME_NOT_ALPHANUM:
  break;
default:
  // yay, it worked
}

Кроме того, с помощью этой техники вы сможете получать побитовые сообщения состояния И и ИЛИ, чтобы вы могли возвращать сообщения о состоянии, которые имеют более одного значения, например DUPLICATE_USERNAME & USERNAME_NOT_ALPHANUM, и обрабатывать их соответствующим образом. Это не всегда хорошая идея, это зависит от того, как вы ее используете.

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