我寫代碼有一條重要的原則:如果有更加直接,更加清晰的寫法,就選擇它,即使它看起來更長,更笨,也一樣選擇它。比如,Unix命令行有一種“巧妙”的寫法是這樣:
~~~
command1 && command2 && command3
~~~
由于Shell語言的邏輯操作`a && b`具有“短路”的特性,如果`a`等于false,那么`b`就沒必要執行了。這就是為什么當command1成功,才會執行command2,當command2成功,才會執行command3。同樣,
~~~
command1 || command2 || command3
~~~
操作符`||`也有類似的特性。上面這個命令行,如果command1成功,那么command2和command3都不會被執行。如果command1失敗,command2成功,那么command3就不會被執行。
這比起用if語句來判斷失敗,似乎更加巧妙和簡潔,所以有人就借鑒了這種方式,在程序的代碼里也使用這種方式。比如他們可能會寫這樣的代碼:
~~~
if (action1() || action2() && action3()) {
...
}
~~~
你看得出來這代碼是想干什么嗎?action2和action3什么條件下執行,什么條件下不執行?也許稍微想一下,你知道它在干什么:“如果action1失敗了,執行action2,如果action2成功了,執行action3”。然而那種語義,并不是直接的“映射”在這代碼上面的。比如“失敗”這個詞,對應了代碼里的哪一個字呢?你找不出來,因為它包含在了`||`的語義里面,你需要知道`||`的短路特性,以及邏輯或的語義才能知道這里面在說“如果action1失敗……”。每一次看到這行代碼,你都需要思考一下,這樣積累起來的負荷,就會讓人很累。
其實,這種寫法是濫用了邏輯操作`&&`和`||`的短路特性。這兩個操作符可能不執行右邊的表達式,原因是為了機器的執行效率,而不是為了給人提供這種“巧妙”的用法。這兩個操作符的本意,只是作為邏輯操作,它們并不是拿來給你代替if語句的。也就是說,它們只是碰巧可以達到某些if語句的效果,但你不應該因此就用它來代替if語句。如果你這樣做了,就會讓代碼晦澀難懂。
上面的代碼寫成笨一點的辦法,就會清晰很多:
~~~
if (!action1()) {
if (action2()) {
action3();
}
}
~~~
這里我很明顯的看出這代碼在說什么,想都不用想:如果action1()失敗了,那么執行action2(),如果action2()成功了,執行action3()。你發現這里面的一一對應關系嗎?`if`=如果,`!`=失敗,…… 你不需要利用邏輯學知識,就知道它在說什么。