我們一開始總會自信的覺得自己寫出來的代碼是個美女,隻是寫着寫着越來越胖,最終寫成了個200斤的胖子,自己見了都嫌棄……
很多人習慣性給方法堆代碼,一個方法少則幾百行多則上千行代碼,好一點的隻是代碼堆的多,爛一點的還嵌套多層條件分支代碼塊,if裡面還有if、if的if裡面還有if……甚至于我們還習慣給自己找理由,看到方法裡面的代碼越來越長卻又總是安慰自己:這次是因為沒時間,下次一定改!
當你真的下定決心要重構它時,你發現這塊邏輯你也看不懂了,至于是什麼時候開始看不懂的你也不知道,此時你看啊看,看了後面忘記前面,一點點的拆分方法時發現這樣拆好像影響後面的邏輯,後面有用到前面的一些變量……開始你心煩了,爆了句尼瑪,轉身出去抽根煙冷靜冷靜……結果重構又不了了之……
方法的拆分重構應該是開發過程中進行的,而不是需求做完之後,更不是有時間之後,沒時間隻是借口而已。
我對代碼有潔癖,而且有強迫症,是以我對自己寫的代碼經常修整。
源于有朋友跟我說不懂怎麼拆分方法代碼合适,下面跟大家分享的是我自己總結的方法拆分原則,非标準,僅供參考:
1.針對重複出現的代碼塊
如果重複出現相同的一行非方法調用代碼或者兩行以上代碼塊,說明這些代碼必然存在一個共同的名稱描述它們,這就可以考慮抽離為獨立方法。如果是非業務代碼還應該考慮是否要抽離到工具類。
相同的一行非方法調用代碼,如集合的判空,以前是這麼寫的:“collection!=null&&!collection.isEmpty()”,可以抽離為一個工具類方法:“CollectionUtils.isEmpty()”,當然,這些常用的工具類一些架構都已經幫我們封裝好了,此處隻是為了舉例。
當一塊代碼在兩個及以上方法中出現時,說明這個代碼塊存在共用性,也必然存在一個名稱來描述它,這時就應該将該代碼塊抽離為獨立方法。這個Idea工具也會有提示,隻不過我們多數人都選擇忽略它,但有強迫症的人一定忽略不了,比如我。
如果隻是多個“屬性”相同的類使用,如實作相同接口的類,那麼就要考慮是否應該抽離到一個抽象類,提供模闆方法,即模闆方法模式。
2.針對條件分支以及循環體代碼塊
方法中出現條件分支代碼塊,且代碼塊超兩行以上的,根據代碼塊是否能用一個名稱來描述決定是否應該拆分為獨立方法。對于嵌套條件分支語句同樣可以繼續拆。
循環語句中的代碼塊同理,如果能将循環代碼塊抽離為獨立方法,那麼通過Stream操作和lambda表達式就可以将代碼改寫為一行。
當一個方法拆出很多相似方法時,可以考慮是否應該用設計模式重寫。本篇隻介紹分法拆分,當然還有設計模式的使用,熟練的使用設計模式加上良好的方法拆分習慣就能寫出漂亮整潔的代碼。
以上兩個方法拆分原則适用于大部分場景,但還是要結合實際情況考慮,理論并不适合生搬硬套。
案例一

注意看,其中的getXxx方法是public的,也就是說會有别的地方調用它們,是以一個讀緩存操作抽離為獨立方法能夠實作代碼複用。其次,讀操作封裝在一個地方,其它地方不需要關心緩存的key是什麼。
另外兩個cacheXxx方法是private的,說明隻有該類中使用,并且我知道也隻有圖中第一個方法調用到,那為啥還抽離為獨立方法?set與get操作抽離為獨立方法放在一塊能夠很清晰的看出是在哪寫入的緩存,調用它的方法看起來也清晰。
根據前面介紹的幾個原則,wxLogin方法其實還沒有拆分完成,wxLogin中的if-else存在一個名稱來描述它,即bindRoleIfNeed,是以還可以将if-else拆分為獨立方法,方法名為bindRoleIfNeed,方法參數為accountId和role。(比較簡單的情況下就沒有必要使用設計模式了)
現在wxLogin方法代碼是不是很整潔、邏輯是不是很清晰,是不是像看文檔一樣,一眼就能看懂。而當我們想要了解細節時再一個方法一個方法看,并且每個方法的邏輯都很清晰。
如果不拆分wxLogin方法,畫面就是下面這個樣子的。
案例二
這是我們網關的一段代碼,雖然是響應式程式設計,但并不妨礙我們了解它。
該方法實作的功能是擷取響應body,解析為字元串,然後做一些替換,再将處理後的json再轉為位元組緩存。
由于Gateway針對傳回參數過長的情況下會分段傳回,是以響應body是一個List<DataBuffer>,我們需要将多個DataBuffer合并為一個,再轉為字元串。
此案例中,将多個List<DataBuffer>轉為字元串可以抽離為工具類方法,拆分後代碼如下。(這裡還隐藏了會導緻響應結果出現中文亂碼的bug)
根據原則2,此方法中依然存在代碼塊:将多個List<DataBuffer>合并為一個,可以用joinDataBuffer去描述它,是以還應該将其抽離為獨立方法。不過架構已經幫我們實作了,是以上面可以改寫成如下。
現在案例二的handleFlux方法可以簡寫為如下。
你看,這不是更清晰可讀了嗎?
當然,并不是将一個方法拆分成多個就行了,方法可不是随便拆的。我去年就看到過一些反例,方法雖然是拆了,但所拆出來的方法并不能讓邏輯清晰,也難以複用,這種就是失敗的方法拆分。
最後
項目代碼整潔=項目架構+設計模式+整潔的方法(函數);
良好的擴充性=項目架構+設計模式,要求高内聚低耦合;
高性能=算法+并發程式設計+對底層的了解(包括架構/中間件實作原理、作業系統底層一些知識(如零拷貝、虛拟記憶體映射等)、JVM、網絡通信等等);
如果按優先級排序,我會這樣排:
整潔易懂的代碼 > 良好的擴充性 > 高性能。
前兩項都包含了項目架構,這也是我喜歡推行DDD的原因。
對于寫業務代碼,如果魚和熊掌不可兼得,那麼我還是那句話,有時候我會為了擴充性以及代碼的整潔易懂而選擇犧牲一點性能,以及多花點時間。
是以空間換時間,還是以時間換空間,是需要看實際情況而定的。
代碼是寫給人看的,更多的時候是給自己看的,何不對自己好點。