如何寫一個清晰明了的Bug
Bug是不可避免。但如何讓自己的Bug寫得清新脫俗,結構清楚則是需要我們不斷努力的。
在開始今天的話題之前,先拋出一個問題,代碼結構好是好事嗎?
代碼結構好事好事嗎?

該圖是我的票圈里一位兄弟轉發的。代碼結構好了,別人接手容易,反倒是寫得爛了,卻可以成為焦點。你咋一聽覺得這是什么神邏輯,雖然聽著有道理,但總感覺有點政治不正確。
這個是一個問題,很值得思考的問題。
寫程序,就是寫邏輯,邏輯最初的樣子,就是用if else來表達,事實上這就是我們描述這個世界的基本方式,if else。這兩個分支可以覆蓋一切情況。你能告訴我們還有if else之外的場景嗎?!

可以這么說,if else可以描述這個世界上所有的邏輯。
if else就是整個世界
你手握if else 兩個單詞,心想,產品你盡管提需求吧,這個世界上還有我if else解決不了的問題嗎。而且久而久之,你會發現寫代碼就是if else,整天就在if else兩個代碼塊里盤旋。
慢慢的,你開始彷徨,開始思考人生,甚至懷疑人生,難道程序這么無聊嗎?if else 外加crud就可以解決一切?
編程屆兩大流派
這個還是要從理論說起,其實描述這個世界有兩種方式。一種是函數算法派,一種面向對象派。

而函數算法派其實就是if else派,這一派是一個古老的門派,他們圍繞著一個方法體(或者叫函數)就可以一直寫下去并且能解決問題。
而面向對象派則主張通過結構和組合的方式來解決問題,而不是圍繞著if else來搞事情。
可以毫不夸張的說,我們現如今絕大多數人也包括我本人在內,我們都還處于函數算法派,也就是if else派。至于面向對象這些東西,充其量注入的時候體會一下,或者在new的時候體會一下。其余時候我們都是在安靜的寫著if else。
然而if else最終讓我們走向不歸路。
前面我們說過if else可以覆蓋整個世界。但覆蓋范圍廣并不等于它明了。

開始的時候,你發現自己的if else異常明了和清晰。

直到后來,一步步,就變成下面這樣了:

直到后來,你開始懷疑人生,于是你決定去看一些技術書籍,讓自己的心靈重新洗滌一下,這樣第二天才能稍微平靜的面對昨日遺留的if else。
避免if else泛濫的四法則:一提二抽三組四模式
那么我們如何避免if else的過渡泛濫呢?我總結了一個法則:一提二抽三組四模式。

1、一提
以下的代碼我是從真實的項目代碼中摘取的。
bad case:

good case:

上面兩段代碼執行了相同的邏輯,但第一種層次更多,可讀性也差,這還是剛開始的代碼,隨著需求的不斷變化,這段代碼的層次結構最后就會變成一堆亂麻。這里其實并沒有用什么技術,就是簡單的對代碼邏輯路徑進行重新的編排,從而實現了代碼的整潔和更好的可讀性。
在if else的優化中,一個核心的思路就是:更少的縮進、更少的else。
上面重構后的代碼,你可以看到縮進線由三條變成了兩條,同時通過把異常場景的邏輯前提的方式去掉了else塊。異常場景邏輯一般指負向的條件,比如==null,notExist,XxxException等。理想的代碼,總是應該把這些異常情況,提前排除掉,然后才安心的去寫主業務邏輯。這樣你的代碼就會顯得層次分明。

理想的代碼應該有的樣子:

二抽
在有限代碼行數內通過第一個法則可以讓結構更加的清晰。當if中的代碼行數過多時,這會就需要把可以獨立成為方法的邏輯抽取成一個private的方法(也可以是public等),代碼過長時,我們總是需要這樣去做,這樣可以讓你的主方法就像一篇文章一樣具有可讀性。

依然是上面的forYes方法。我們把exist的邏輯單獨抽取了一個方法,同時又把主邏輯代碼也抽了一個方法,你會發現forYes1方法的代碼長度并沒有增加多少,依然保持的良好的可讀性。
三組
上面的第二法則是抽取一個private方法,還沒有出類。當我們抽取到一定地步,會發現適合單獨成類的時候,應該把之前的這些private方法移動到一個新的類中。這個就是第三法則:組合,通過組合的方式來構建你的邏輯。



四模式
在用完組合后,我們的代碼其實已經基本上比較清楚了。但為了讓我們的代碼更加的優雅,更上一個層次。有的場景下你需要使用到設計模式,設計模式被總結成為最佳實踐,不是僅僅用來寫框架用的,寫日常紛繁復雜的業務邏輯代碼也是需要設計模式的。
接下來我就以自己正在開發的項目中的場景為例,來說說如何使用設計模式改善你的既有代碼。
在項目中我們需要為審批工作流提供一個回調(callback)接口。審批流有不同的狀態,不同的狀態回調會執行不同的邏輯。在重構前的代碼大體是這樣的:

我們希望最終的樣子是這樣的:

首先新建一個State接口類:

然后新建三個實現狀態,分別是Yes,No和Cancel:

然后新建一個Context類:

然后,新建一個State工廠類:

改造完畢。通過上面的重構,我們使用了狀態模式和一點點工廠模式。最終callback方法就只需通過newInstance就可以找到具體狀態的回調邏輯,而以后即使狀態在不斷的增加的,你也只需新建一個新的實現狀態,然后注入工廠類中,做到了可插拔。值得注意的是,這里我們的state其實并沒有很好的被傳遞和持有,這很不“狀態”,這通過這樣的方式我們實現了對callback的重構,結構也更加的清晰了。總之,當你遇到業務需求的不斷變化,你需要找到一種合適的設計模式來hold住它,即使GOLF不能滿足你的需求,你也可以自己創造一個設計模式來讓你的代碼清晰易懂。
總結
本文一開始介紹了if else泛濫后的問題。然后為你介紹了“一提二抽三組四模式”法則。我們的項目也是萬事萬物中的一部分,套用《規模》一書中的一段話:耗散力一直在持續且不可避免地做著功,這使得所有系統都將退化。設計最為精巧的機器、最具創新組織力的公司、進化得最完美的生物都無法逃脫這一最為嚴酷的死神。耗散力就是一個系統運轉所產生的摩擦力,系統復雜性所帶來的無序熱量,你的代碼也一樣,也需要通過不斷的改進和重構來盡量的延緩和降低熵的增長。
最終我們都將屈服于各種形式的磨損和衰竭,熵能殺人,你需要吃飯!