Замена IF логическим выражением в PHP

Я рефакторинга некоторые старый код, когда я наткнулся на конструкцию, похожую на эту:

// function bar() returns a value
// if the value is an instance of customException class, terminate with error code
// else process the regular data

$foo = bar();
checkForException($foo) && exit($foo->errorCode());
process($foo);

теперь странно, как это может показаться, это намного короче, чем

$foo=bar();
if(checkForException($foo)) {
    exit($foo->errorCode();
}
else {
    process($foo);
}

и несколько более читаемый (по крайней мере, после первоначального удивления), то

$foo=bar();
(checkForException($foo)) ? exit($foo->errorCode()) : process($foo);

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

другими словами, вместо

if($foo) {
    bar();
}
else {
    // there is no real reason for this to exist, since 
    // I have nothing to write here, but I want to conform 
    // to the general coding practices and my coding OCD
}

можно просто писать

$foo && bar();

так что причина в не вижу много пользы? Может ли это быть так же просто, как "не изобретать колесо, писать более читаемый if/else, и если вы действительно хотите его сократить, это то, для чего нужен тернарный оператор"?

EDIT: пожалуйста, имейте в виду, что приведенный выше код был быстро получен из исходного кода и должен был быть просто примером использования кода "короткого замыкания". Если можете, пожалуйста воздержитесь от предложения улучшений кода, поскольку это не было желаемым результатом вопроса.

Пример № 2

userCheckedTheBox($user) && displayAppropriateInfo();

5 ответов


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

myFunction( ) || exitWithError( "Uh-oh" )

при кодировании в срок, и когда пользовательский интерфейс не должен быть звездным, это быстрый способ избежать ошибок.

стиль также популярен в JavaScript для параметров по умолчанию:

function myfunction(foo) {
    foo = foo || 0;
    // note that a non-zero default won't work so well,
    // because the user could call the function with 0
}

и для null-проверки:

var bar = foo && foo.property;

Я нахожу, что как только вы привыкнете к нему, это очень читабельно и часто более интуитивным, чем if/else или ?:. Но вы должны использовать его только тогда, когда это имеет смысл. Использование его везде будет очень запутанным. Например, в вашем примере, вы должны не использовать его. Лично я использую его для простой проверки ошибок и некоторые значения по умолчанию. В больших проектах вы почти всегда хотите сделать намного больше, когда возникает ошибка, поэтому в этих случаях вы не должны использовать это.

также вы должны быть осторожны; это работает только в языки, которые имеют оценку короткого замыкания (http://en.wikipedia.org/wiki/Short-circuit_evaluation). А иногда ... --7--> и or короткое замыкание, в то время как && и || нет.

myfunction() or die("I'm melting!"); также вполне удовлетворительно писать.

и, наконец, пустой else блоки, как правило, это то, что я никогда раньше не видел или не слышал, чтобы кто-то рекомендовал. Мне это кажется бессмысленным. Наиболее читаемый вариант для вашего примера, довольно просто:

if( $foo ) {
    bar( );
}

пока $foo && bar(); меньше строк кода, это гораздо менее читаемым. Упрощение понимания кода обычно более важно, чем уменьшение общего LoC. Даже если вы не работаете в среде с несколькими программистами, вам придется вернуться и прочитать свой код в какой-то момент в будущем, и вы, вероятно, не сможете вспомнить, какое обоснование было за каждой строкой кода (Результаты поиска по!--5-->).

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

вот одно приемлемое использование для такого кода:

$isValidUser = $userName && usernameIsValid();

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


по ошибки, вы должны использовать реальные исключения:

try {
  $foo = bar();
} catch(FooException $e) {
  exit($e->errorCode);
}
process($foo);

посмотреть документация для errorhandling.


что делает этот код, возвращая экземпляр CustomException просто не складывается. Почему бы немного не изменить определение функции:

function bar()
{
     $stuff = true;
     if ($stuff === true)
     {
         return 'Value on success';
     }
     //something went wrong:
     throw new CustomException('You messed up');
}
try
{//here's the outlandish try-catch block
     $foo = bar();
}
catch (CustomException $e)
{
    exit($e->message());//fugly exit call, work on this, too
}
//carry on here, no exception was thrown

кроме того, вызов этой второй функции (checkForException($foo)) - это просто абсурд. Функциональные вызовы дешевы, но не бесплатны. Вы хотите знать, вернула ли функция экземпляр CustomException? Не превращайте это в функцию, но используйте instanceof. Используя короткое замыкание, чтобы сохранить количество символов (ad, таким образом, время разбора) вниз, в то же время тратя ресурсы на всех других уровнях примерно так же глупы, как появление в V8 mustang на курсе hyper-miling.


еще одно возможное решение вашей проблемы:

$foo = bar();
!checkForException($foo) or exit($foo->errorCode);
process($foo);

но лучше изменить !checkForException на isNoException или что-то в этом роде.