阿里巴巴 Java 開發手冊評述

ThoughtWorks發表於2017-04-07

注:本文基於阿里巴巴技術手冊的1.0.2版本編寫

2016年底,阿里巴巴公開了其在內部使用的Java程式設計規範。隨後進行了幾次版本修訂,筆者當時看到的版本為v1.0.2版。下載地址可以在其官方社群——雲棲社群找到。

筆者作為一名有數年工作經驗的Java程式設計師,仔細研讀了這份手冊,覺得是一份不可多得的好材料。正如阿里巴巴在釋出時所說,“阿里巴巴集團推出的《阿里巴巴Java開發手冊(正式版)》是公司近萬名開發同學集體智慧的結晶,以開發視角為中心,詳細列舉了如何開發更加高效、更加容錯、更加有協作性,力求知其然,更知其不然。結合正反例,讓Java開發者能夠提升協作效率、提高程式碼質量。” 同時,阿里巴巴也期望這套Java統一規範標準將有助於提高行業編碼規範化水平,幫助行業人員提高開發質量和效率、大大降低程式碼維護成本。

(圖片來自:http://t.cn/R63jrWi)

其實早在多年前,Google就已經把公司內部採用的所有語言的編碼規範(其稱為Style Guide)都開源在Github上。這份清單中包括了C++Objective-CJavaPythonRShellHTML/CSSJavaScriptAngularJSCommon LispVimscript等語言的程式設計規範。並且Google還發布了一個用於檢查樣式合規性的工具cpplint以及在Emacs中使用Google程式設計樣式的配置檔案google-c-style.el。看來Google中Emacs粉比Vim粉要強勢的多。

Google為什麼要釋出這樣的Style Guide呢?因為它認為幾乎所有的開源專案都需要有一組約定來規範如何編寫程式碼。如果專案中的程式碼都能保持一致的風格,那麼即使程式碼再多也會很容易的被人理解。

Google的這份程式設計規範包含了很多方面,從”對變數使用camelCase命名法”到”絕不要使用全域性變數”到”絕不允許例外“等。其Java程式設計規範包含7大部分,分別為介紹、原始檔基本要求、原始檔結構、格式化、命名、程式設計實踐和Javadoc。每一部分又細分為很多子條目。如果採取條規範的原因不是很容易理解,都會配有相應的示例或者引用文章。

由於Google的這份程式設計規範目前只有英文版本,所以在中國的程式設計師中只有少部分人知道它的存在。並且只有更少的團隊在真正的應用它,其中就包括我的團隊。我們團隊根據Google的Java style guide也演化出了自己的團隊版本,放置在團隊共享wiki上供大家隨時查閱。我們根據自身的專案特點豐富了”程式設計實踐”裡的內容,並且新加入一個章節來描述編寫Java程式碼的一些原則,比如簡潔程式碼、組合優於繼承、stream優於for迴圈等。

我想阿里巴巴釋出的Java開發手冊之所以叫做”開發手冊”,而不是像Google那樣叫做“Style Guide(樣式風格)”,是因為它不僅僅侷限於style guide這一方面,而是以Java開發者為中心視角,劃分為程式設計規約、異常日誌規約、MYSQL規約、工程規約、安全規約五大塊,再根據內容特徵,細分成若干二級子目錄。根據約束力強弱和故障敏感性,規約依次分為強制、推薦、參考三大類。

該開發手冊中的每一條都值得了解。限於篇幅原因,這裡只列出”程式設計規約“中有感受的幾條來評述一下。

15. 【參考】各層命名規約:

A) Service/DAO 層方法命名規約

1) 獲取單個物件的方法用 get 做字首。

2) 獲取多個物件的方法用 list 做字首。

3) 獲取統計值的方法用 count 做字首。

4) 插入的方法用 save(推薦)或 insert 做字首。

5) 刪除的方法用 remove(推薦)或 delete 做字首。

6) 修改的方法用 update 做字首。

B) 領域模型命名規約

1) 資料物件:xxxDO,xxx 即為資料表名。

2) 資料傳輸物件:xxxDTO,xxx 為業務領域相關的名稱。

3) 展示物件:xxxVO,xxx 一般為網頁名稱。

4) POJO 是 DO/DTO/BO/VO 的統稱,禁止命名成 xxxPOJO。

命名規約的第15條描述了在Service/DAO層對於資源的操作的命名規範。這一條的參考價值極大,因為我所有待過的團隊對於這一點都沒有明顯的約束,每個團隊都有五花八門的實現。如果能遵守這一點,那麼我們在操作資源時就會減少一些困擾。

2. 【強制】long 或者 Long 初始賦值時,必須使用大寫的 L,不能是小寫的 l,小寫容易跟數字1混淆,造成誤解。

說明:Long a = 2l; 寫的是數字的 21,還是 Long 型的 2?

這是常量定義的第2條。從這一點可以看出阿里巴巴對程式碼可讀性的細節扣的很嚴格。我也很贊同這一點。程式碼只需編寫一次,而會被檢視無數次,所以要力爭在第一次編寫的時候儘可能少的引入歧義。

1. 【強制】大括號的使用約定。如果是大括號內為空,則簡潔地寫成{}即可,不需要換行;如果是非空程式碼塊則:

1) 左大括號前不換行。

2) 左大括號後換行。

3) 右大括號前換行。

4) 右大括號後還有 else 等程式碼則不換行;表示終止右大括號後必須換行。

格式規約的第1條終於終結了括號之爭。這一條需要強制遵守,那麼左大括號換行一派則被徹底排除在阿里巴巴之外。有人說不推薦左大括號換行,可以減少行數,增加單個螢幕可以顯示的程式碼行數。而有的人反駁說現在螢幕已經足夠大,不換行則破壞了對稱之美。其實對於我來說兩種格式都有各自的好處,我都可以接受,只要團隊能夠堅持使用其中之一即可。

5. 【強制】縮排採用 4 個空格,禁止使用 tab 字元。

說明:如果使用 tab 縮排,必須設定 1 個 tab 為 4 個空格。IDEA 設定 tab 為 4 個空格時,請勿勾選 Use tab character;而在 eclipse 中,必須勾選 insert spaces for tabs。

正例: (涉及 1-5 點)

使用空格代替tab字元進行縮排已經成為了程式設計界的共識。其主要原因是不同的平臺甚至不同的編輯器下tab字元的長短是不一樣的。不過Google在其《java style guide》中規定縮排為2個空格,而阿里巴巴約定為4個空格。由於4個空格的縮排比2個空格的縮排長一倍,所以如果在程式碼巢狀過深的情況下可能會很快超過單行最多字元數(阿里巴巴規定為120個)的限制。不過這個問題可以從另一個方面進行思考,如果由於縮排的原因導致單行字元數超標,這很可能是程式碼設計上有壞味道而導致巢狀過深。所以最好從調整程式碼結構的方面下手。

6. 【強制】單行字元數限制不超過 120 個,超出需要換行,換行時遵循如下原則:

1) 第二行相對第一行縮排 4 個空格,從第三行開始,不再繼續縮排,參考示例。

2) 運算子與下文一起換行。

3) 方法呼叫的點符號與下文一起換行。

4) 在多個引數超長,逗號後進行換行。

5) 在括號前不要換行,見反例。

正例:

反例:

關於換行,Google並沒有給出明確的要求,而阿里巴巴則給出了強制性的要求。Google特別提示可以通過一些重構手法來減少單行字元長度從而避免換行,這一點我頗為認同。關於引數,很多方法呼叫超過120個字元需要換行,這暴露除了過長引數列的程式碼壞味道,解決方式之一就是使用重構手法的Replace Parameter With Method的方式把一次方法呼叫化為多次方法呼叫,或者使用Introduce Parameter Object手法創造出引數物件並進行傳遞。

17. 【推薦】迴圈體內,字串的聯接方式,使用 StringBuilder 的 append 方法進行擴充套件。 反例:

說明:反編譯出的位元組碼檔案顯示每次迴圈都會 new 出一個 StringBuilder 物件,然後進行append 操作,最後通過 toString 方法返回 String 物件,造成記憶體資源浪費。

這是《Effective Java》以及其他文章中經常提及的優化方式,而且面試初級Java工程師時幾乎是一個必考點。其實不僅是在迴圈體內,所有需要進行多次字串拼接的地方都應該使用StringBuilder物件。

20. 【推薦】類成員與方法訪問控制從嚴:

1) 如果不允許外部直接通過 new 來建立物件,那麼構造方法必須是 private。

2) 工具類不允許有 public 或 default 構造方法。

3) 類非 static 成員變數並且與子類共享,必須是 protected。

4) 類非 static 成員變數並且僅在本類使用,必須是 private。

5) 類 static 成員變數如果僅在本類使用,必須是 private。

6) 若是 static 成員變數,必須考慮是否為 final。

7) 類成員方法只供類內部呼叫,必須是 private。

8) 類成員方法只對繼承類公開,那麼限制為 protected。

說明:要嚴控類、方法、引數、變數的訪問範圍。過寬泛的訪問範圍不利於模組解耦。思 考:如果是一個 private 的方法,想刪除就刪除,可是一個 public 的 Service 方法,或者一個 public 的成員變數,刪除一下,不得手心冒點汗嗎?變數像自己的小孩,儘量在自己的視線內,變數作用域太大,會無限制的到處跑,那麼你會擔心的。

這其實就是經典的原則‘Principle of least privilege’ 的體現。我們必須遵循這一原則,但不知為何阿里巴巴將其級別列為“推薦”。

7. 【參考】方法中需要進行引數校驗的場景:

1) 呼叫頻次低的方法。

2) 執行時間開銷很大的方法,引數校驗時間幾乎可以忽略不計,但如果因為引數錯誤導致 中間執行回退,或者錯誤,那得不償失。

3) 需要極高穩定性和可用性的方法。

4) 對外提供的開放介面,不管是 RPC/API/HTTP 介面。

5) 敏感許可權入口。

8. 【參考】方法中不需要引數校驗的場景:

1) 極有可能被迴圈呼叫的方法,不建議對引數進行校驗。但在方法說明裡必須註明外部參 數檢查。

2) 底層的方法呼叫頻度都比較高,一般不校驗。畢竟是像純淨水過濾的最後一道,引數錯 誤不太可能到底層才會暴露問題。一般 DAO 層與 Service 層都在同一個應用中,部署在同一臺伺服器中,所以 DAO 的引數校驗,可以省略。

3) 被宣告成 private 只會被自己程式碼所呼叫的方法,如果能夠確定呼叫方法的程式碼傳入引數已經做過檢查或者肯定不會有問題,此時可以不校驗引數。

編寫程式碼時,對引數進行校驗是不可避免的。詳細說又扯到“防禦式程式設計”和“契約式程式設計”的話題上。這兩項之所以列為參考,並沒有強迫大家遵守。

6. 【推薦】與其“半吊子”英文來註釋,不如用中文註釋把問題說清楚。專有名詞與關鍵字保持英文原文即可。

反例:“TCP 連線超時”解釋成“傳輸控制協議連線超時”,理解反而費腦筋。

看到這一條我已經笑出來了。這一條說的很好,註釋是用來闡述問題的,如果看了註釋還一頭霧水,那麼這樣的註釋不要也罷。使用中文沒什麼可丟人的,解決問題才是王道。

7. 【推薦】程式碼修改的同時,註釋也要進行相應的修改,尤其是引數、返回值、異常、核心邏輯等的修改。

說明:程式碼與註釋更新不同步,就像路網與導航軟體更新不同步一樣,如果導航軟體嚴重滯後, 就失去了導航的意義。

阿里巴巴對該條的說明非常到位。其實我們團隊在編寫程式碼時預設是沒有任何註釋的,因為我們追求的是self-explanatory methods。即程式碼本身已經就能說明它的用途。只有在很少的情況下需要新增註釋。


程式設計規約的第九部分都是很好的tips,值得去了解和學習。

除了程式設計規約之外,日誌規約、MySQL規約、工程規約和安全規約也都有極高的參考價值,這也是比Google的Java Style Guide出色的地方。這裡就不再評述了。


阿里巴巴公佈這個Java開發手冊絕對是值得讚賞的事情。最後我也想給其提幾點建議:

  1. 建議使用公開wiki的方式釋出該手冊,而不是採用pdf的方式。因為如果像google那樣是公開wiki方式的話,可以方便大家參與修正和改進,並且可以看到版本歷史。
  2. 該手冊並沒有明確的版權許可,只是在頁尾處加入了“禁止用於商業用途,違者必究”的字樣。Google的style guide的版權為CC-By 3.0 License,建議阿里巴巴能夠指明其版權。
  3. 手冊中的部分示例程式碼並沒有遵守其列出的程式設計規約,有點打臉之嫌。比如以下示例程式碼:

    其while和if關鍵字與小括號之間並沒有空格,違反了該手冊中3. 【強制】if/for/while/switch/do 等保留字與左右括號之間都必須加空格。這一規則。
  4. 集合處理中可以多推薦一些Java8的集合操作方法。
  5. 有些名詞沒有過多解釋,比如很多人可能都不知道什麼叫一方庫、二方庫。
  6. 希望除了這份開發手冊以外,阿里巴巴也可以推出對應的checkstyle配置檔案以及Intellij、Eclipse的配置檔案。畢竟格式化這些事都可以交由IDE來解決,通過在構建時使用checkstyle外掛也可以防止不合規的程式碼遷入到倉庫,從源頭上保證程式碼樣式的一致性。

最後,希望這份Java開發手冊可以持續改進,吸納百家之長,成為每個入門程式設計師必看的手冊。

相關文章