減少代碼嵌套
“這家伙的代碼太差勁了!”當我們遇到我們不喜歡的代碼時常常這么說或是這么想。有時候時因為代碼有很多 bug,有時候是因為不喜歡它的代碼風格,而有時候僅僅只是因為感覺不舒服。最近我仔細想了想,然后突然意識到或許是因為開發者是個新手。這些代碼有個明顯不招人喜歡的地方:太多的嵌套。當我思考的越多,我越來越意識到,這不是那些我們以前經常討論的事情。
那現在讓我們討論討論這個吧。我首先想說的是什么叫代碼嵌套,為什么會影響代碼的質量。然后我將用這些年學的一些技巧來減少嵌套。
什么是代碼嵌套,為什么嵌套是不好的?
演示比空口而談更為容易。很抱歉用下面這個不自然的例子來解釋什么是深層的代碼嵌套。
你僅僅從每行的縮進中就可以看到這段巨大的代碼有多少嵌套。在我們最終判斷“用戶不能被找到”前,我們必須穿過 4 個條件語句,而且每個條件句都嵌套在先決條件句中。
我覺得這是一段糟糕的代碼。每個額外嵌套的內容大腦都必須另外記錄。每一個嵌套的塊你都得關注它是和哪個條件句關聯的(即使你的編輯器能幫助你,但是卻不能徹底的解決這個問題)。這僅僅是一個最后返回 user 的簡單直觀的例子,讓我們看一個復雜點的例子:
對我來說,即使只是寫這個例子都顯得非常的難。這很明顯不自然或者別的什么,但是關鍵是弄懂它在實現什么都很難。不像之前的例子,這個不是簡單的嵌套然后返回;它嵌套然后不嵌套,然后又嵌套,最后返回結果。
怎么避免代碼嵌套
我發現最好的避免代碼嵌套的方法就是盡早返回結果。緩存對這來說是一個很好的例子。與其測試緩存是否失敗然后在條件句中再從數據庫獲取信息,不如判斷緩存是否成功然后盡早的返回結果。
代碼如下:
在這個簡單的例子中,好像沒有改進多少,但是如果將這個技巧運用到第一個例子中會發生什么?它將顯著的得到改進:
這不僅使代碼閱讀起來簡單,使我們少關注一些上下文的聯系,讓代碼編輯器少做些自動換行的工作,而且使代碼容易分塊并更容易進行注釋。
那我們還能使用哪些別的技巧來避免代碼嵌套呢?這更多的取決于具體情況。你寫一大堆回調函數的時候是不是也容易嵌套?如果是,你通常可以重構你的代碼,多用命名函數而不是匿名函數。下面是重構前的效果:
在第一個回調函數中,我們運用了早點返回結果的技巧,但是就如你看到的一樣還是有很多嵌套。現在如果我們轉為使用命名函數會怎樣呢?
這在嵌套方面得到了明顯改善。不幸的是我們得寫一個名為 curry 的輔助函數,但這僅僅只用寫一次然后能被所有寫在同一類型的代碼所復用。同樣不幸的是我仍然覺得這種代碼很難閱讀,這就是我避免寫過多的回調類型函數的原因。不管怎樣,最起碼你能減少嵌套了。老實說,可能有更好的我沒想到的減少嵌套的方法。如果你能用 JS 以一種更好的方式重寫 getCachedUser 函數,請發博客!
減少代碼嵌套的另外一種方法就是非配一個中間變量。下面這個例子里是 Erlang 語言的一些對文件進行操作的函數,一個 case 語句嵌套在另一個 case 語句中。
我們可以分配一個名為“Resp”的中間變量,然后把第二 case 語句代入到函數的主代碼中,像這樣:
這是什么意思呢?
最后,不是說懂得這些就能造就或是毀滅一個程序員。事實上,我并沒有要改變代碼的邏輯,只是簡化它的實現方式。這只是你在寫代碼或者看別人的代碼的時候回想到的一些簡單的事。真心希望你能和我一樣覺得減少嵌套是一個極好的目標,并希望你能找到越來越多的方法去實現它。