用逻辑表达式替换PHP中的IF语句

8

我正在重构一些旧代码时,偶然发现了一个类似于这样的结构:

// 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);

虽然看起来很奇怪,但这比之前的要短得多。

在IT技术方面。
$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语句,如果你真的想缩短代码,那就用三目运算符”这样简单吧。

编辑:请注意,上面的代码是从原始代码中快速派生出来的,只是用于说明“短路”代码的用法。如果可以,请不要建议代码改进,因为这不是问题的预期结果。

示例2

userCheckedTheBox($user) && displayAppropriateInfo();

1
在一个结构化框架中,你不会经常使用 exit。此外,完整的 if/else 允许您在 else 分支中使用多个指令 - 但有时短路运算非常有用,只是不要滥用它。 - moonwave99
那么,为什么这种方法没有被广泛使用的原因是什么呢?因为有不同类型的程序员:一些人编写“酷炫”的代码,而另一些人则编写易于维护的代码。 - zerkms
1
对不起,但是_return value_是一个异常对象的实例?为什么没有抛出异常呢?这样,你就可以拥有那个非常清晰、一点也不奇怪的throw-catch块了。 - Elias Van Ootegem
1
@Elias Van Ootegem:如果代码的原始作者抛出了异常(正如预期的那样),那么他就没有机会使用他前一天学到的新技巧了。 - zerkms
1
@user1853181:不抛出异常就像是最愚蠢的事情...在我看来,指出这一点并不是离题的。讨论三元运算符的使用以及其他各种简写编码技巧都不应该优先于异常处理的误用... - Elias Van Ootegem
显示剩余3条评论
5个回答

6
$foo && bar();虽然代码行数较少,但可读性较差。让你的代码易于理解通常比减少总代码行数更重要。即使你没有与多名程序员一起工作的环境,你也将不得不在未来某个时候回来阅读你的代码,并且你可能无法记住每行代码背后的原理(Eagleson's Law)。
一般来说,你应该仅在程序员意图绝对明确的情况下使用这些语句。在我看来,测试条件和主动修改程序当前状态的代码位于同一语句中是非常糟糕的做法。
以下是这种代码的一个可接受用法:
$isValidUser = $userName && usernameIsValid();

在这里,&& 运算符的两侧都在测试一个条件。右侧调用函数进行测试并不影响代码可读性。


3

有一种古老的技术,我相信在拼凑的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
}

并且用于空值检查:

var bar = foo && foo.property;

我发现一旦你习惯了它,它就非常易读,通常比if/else?:更直观。但你只有在有意义的时候才应该使用它。到处都使用它会使事情变得非常混乱。例如,在你的例子中,你不应该使用它。个人而言,我用它来检查简单的错误和一些默认值。在大型项目中,当出现错误时,你几乎总是希望做更多的事情,因此在这些情况下不应使用它。
另外,你应该小心;这仅适用于具有短路评估(http://en.wikipedia.org/wiki/Short-circuit_evaluation)的语言。有时andor是短路的,而&&||则不是。
myfunction() or die("I'm melting!");也很满意。
最后,我从未见过或听说有人推荐空的else块作为规则。对我来说似乎非常无意义。对于您的示例来说,最可读的选项是非常简单的:
if( $foo ) {
    bar( );
}

1

对于错误,应该使用真正的异常:

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

请查看错误处理文档


1
OP正在使用真正的异常(customException),只是它们没有被抛出,而是作为返回值使用... - Elias Van Ootegem

1
无论那段代码在做什么,返回 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。在保持字符数(因此解析时间)的同时浪费其他所有级别的资源,这样做就像在超低油耗路段上开V8野马一样愚蠢。

我已经修改了我的问题,以更好地反映所需的结果。异常正在被抛出和处理,名称只是有点不幸。 - user1853181
@user1853181:很抱歉一直这样强调,但显然你没有抛出异常。如果你抛出了异常,短路评估将会失败,你只会看到一个消息,上面写着“错误:未捕获的异常”。 - Elias Van Ootegem
就我所理解的,bar() 中的异常处理程序正在记录异常信息并将自定义对象(其中包含错误代码和其他信息)返回给调用者。我相信这背后有一个良好的意图,现在正在尝试弄清楚...但如果需要,这确实是另一个问题的话题。 - user1853181

0

你的问题的另一个可能解决方案:

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

但最好将!checkForException更改为isNoException或类似的内容。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接