1. 程式人生 > >推薦《Clean Code》一書,讓你成為優秀的程式設計師

推薦《Clean Code》一書,讓你成為優秀的程式設計師

曾經維護過有十多年曆史的銀行系統,也全新開發過多模組的企業應用系統。經歷過各種各樣寫法的程式碼,有的難以維護,有的清晰明瞭,有的埋下深坑… 在我的團隊裡,我總是向新人灌輸程式碼整潔之道的思想,我時刻告訴他們,功能完成只是最基本的要求,更重要的是你能把程式碼清晰明白地表達出業務邏輯,別人容易看得懂你寫的程式碼。
對於提升程式碼構建水平,Clean Code - 程式碼整潔之道,好書值得推薦一讀。

下面是摘自美團技術團隊作者的部分歸納,作者的思想和我看完書後的感覺完全一致,分享給大家:

clean code,顧名思義就是整潔的程式碼,或者說清晰、漂亮的程式碼,相信大多數工程師都希望自己能寫出這樣的程式碼。

也許這是個千人千面的話題,每個工程師都有自己的理解。比如我,從一個天天被罵程式碼寫得爛的人,逐漸學習成長,到現在也能寫的出“人模人樣”的程式碼來了。這期間算是積累了一點經驗心得,想和大家分享,拋磚引玉。

本文主要針對面向物件程式設計的clean code來闡述,面向過程程式碼的思路會比較不同,不在本文的討論範疇。

程式碼大部分時候是用來維護的,而不是用來實現功能的

這個原則適用於大部分的工程。我們的程式碼,一方面是編譯好讓機器執行,完成功能需求;另一方面,是寫給身邊的隊友和自己看的,需要長期維護,而且大部分專案都不是朝生夕死的短命鬼。

大部分情況下,如果不能寫出清晰好看的程式碼,可能自己一時爽快,後續維護付出的代價和成本將遠高於你的想象。

對清晰好看程式碼的追求精神,比所有的技巧都要重要。

優秀的程式碼大部分是可以自描述的,好於文件和註釋

當你翻看很多開原始碼時,會發現註釋甚至比我們自己寫的專案都少,但是卻能看的很舒服。當讀完原始碼時,很多功能設計就都清晰明瞭了。通過仔細斟酌的方法命名、清晰的流程控制,程式碼本身就可以拿出來當作文件使用,而且它永遠不會過期。

相反,註釋不能讓寫的爛的程式碼變的更好。如果別人只能依靠註釋讀懂你的程式碼的時候,你一定要反思程式碼出現了什麼問題(當然,這裡不是說大家不要寫註釋了)。

說下比較適合寫註釋的兩種場景:
public interface,向別人明確釋出你功能的語義,輸入輸出,且不需要關注實現。
功能容易有歧義的點,或者涉及比較深層專業知識的時候。比如,如果你寫一個客戶端,各種config引數的含義等。

程式碼整潔的常見技巧

1.單一職責

這是整潔程式碼的最重要也是最基本的原則了。簡單來講,大到一個module、一個package,小到一個class、一個method乃至一個屬性,都應該承載一個明確的職責。要定義的東西,如果不能用一句話描述清楚職責,就把它拆掉。

我們平時寫程式碼時,最容易犯的錯誤是:一個方法幹了好幾件事或者一個類承載了許多功能。

先來聊聊方法的問題。個人非常主張把方法拆細,這是複用的基礎。如果方法幹了兩件事情,很有可能其中一個功能的其他業務有差別就不好重用了。另外語義也是不明確的。經常看到一個get()方法裡面竟然修改了資料,這讓使用你方法的人情何以堪?如果不點進去看看實現,可能就讓程式陷入bug,讓測試陷入麻煩。

再來聊聊類的問題。我們經常會看到“又臭又長”的service/biz層的程式碼,裡面有幾十個方法,幹什麼的都有:既有增刪改查,又有業務邏輯的聚合。每次找到一個方法都費勁。不屬於一個領域或者一個層次的功能,就不要放到一起。

我們team在code review中,最常被批評的問題,就是一個方法應該歸屬於哪個類。

2.清晰的命名

老生常談的話題,這裡不展開講了,但是必須要mark一下。有的時候,我思考一個方法命名的時間,比寫一段程式碼的時間還長。原因還是那個邏輯:每當你寫出一個類似於”temp”、”a”、”b”這樣變數的時候,後面每一個維護程式碼的人,都需要用幾倍的精力才能理順。

並且這也是程式碼自描述最重要的基礎。

3.避免過長引數

如果一個方法的引數長度超過4個,就需要警惕了。一方面,沒有人能夠記得清楚這些函式的語義;另一方面,程式碼的可讀性會很差;最後,如果引數非常多,意味著一定有很多引數,在很多場景下,是沒有用的,我們只能構造預設值的方式來傳遞。

解決這個問題的方法很簡單,一般情況下我們會構造paramObject。用一個struct或者一個class來承載資料,一般這種物件是value object,不可變物件。這樣,能極大程度提高程式碼的可複用性和可讀性。在必要的時候,提供合適的build方法,來簡化上層程式碼的開發成本。

4.過長方法和類

一個類或者方法過長的時候,讀者總是很崩潰的。簡單地把方法、類和職責拆細,往往會有立竿見影的成效。以類為例,拆分的維度有很多,常見的是橫向/縱向。例如,如果一個service,處理的是跟一個庫表物件相關的所有邏輯,橫向拆分就是根據業務,把建立/更新/修改/通知等邏輯拆到不同的類裡去;而縱向拆分,指的是把資料庫操作/MQ操作/Cache操作/物件校驗等,拆到不同的物件裡去,讓主流程儘量簡單可控,讓同一個類,表達儘量同一個維度的東西。

5.讓相同長度的程式碼段表示相同粒度的邏輯

這裡想表達的是,儘量多地去抽取private方法,讓程式碼具有自描述的能力。舉個簡單的例子

 public void doSomeThing(Map params1,Map params2){
   Do1 do1 = getDo1(params1);
   Do2 do2 = new Do2();
   do2.setA(params2.get("a"));
   do2.setB(params2.get("b"));
   do2.setC(params2.get("c"));
   mergeDO(do1,do2);
   }
   private void getDo1(Map params1);
   private void mergeDo(do1,do2){...};

類似這種程式碼,在業務程式碼中隨處可見。獲取do1是一個方法,merge是一個方法,但獲取do2的程式碼卻在主流程裡寫了。這種程式碼,流程越長,讀起來越累。很多人讀程式碼的邏輯,是“廣度優先”的。先讀懂主流程,再去看細節。類似這種程式碼,如果能夠把構造do2的程式碼,提取一個private 方法,就會舒服很多。

以上列出的是最基本要求的紮實技能。

本文屬轉載,請尊重原著。
原創2017-01-19王燁美團點評技術團隊美團點評技術團隊
聯絡郵箱:[email protected]