2017年開春,阿里對外公佈了「阿里巴巴Java開發手冊」從頭至尾瀏覽了一遍這份手冊以後,感受很棒。雖然其中的某些觀點筆者不能苟同,但大部分的規範仍是值得絕大多數程序員學習和遵照的。javascript
筆者將對這份代碼規範中的一些細節作一些解讀,包含筆者的觀點和想法,能夠做爲這份代碼規範的擴展閱讀。對於規範中某些「顯而易見」的條款,將不在解讀範圍之列(換言之,這都不懂,就說明你天賦不夠,乘早別作程序員了)。html
固然,筆者在平常的編程過程當中屬於「代碼潔癖偏執狂」,因此文中的某些觀點僅表明我的見解,請勿人生攻擊。java
阿里官方代碼規範解讀系列總計五篇,已在本公衆號發過,本篇爲合集,對以前文章中的部份內容做了修訂。程序員
命名規約面試
1.1.1 代碼中的命名均不能如下劃線或美圓符號開始,也不能如下劃線或美圓符號結束 1.1.2 代碼中的命名嚴禁使用拼音與英文混合的方式,更不容許直接使用中文的方式 1.1.3 / 1.1.4 類名使用UpperCamelCase風格,必須聽從駝峯形式(某些狀況諸如領域模型相關的命名除外);方法名、參數名、成員變量、局部變量都統一使用lowerCamelCase風格,必須聽從駝峯形式 1.1.5 常量命名所有大寫,單詞間用下劃線隔開 1.1.9 包名統一使用小寫,點分隔符之間有且僅有一個天然語義的英語單詞
上述規則,主要是規定了你書寫Java的時候,哪些字符能夠用,何時用大寫,何時用小寫。應該說,絕大多數寫Java的都遵循着上述的規範,就像筆者說的:尼瑪這都不懂,乘早改行別寫Java了。spring
筆者在實際編程過程當中,對類名的風格,可能更激進一些,根據阿里的規範:數據庫
類名使用UpperCamelCase風格,必須聽從駝峯形式,但如下情形例外:(領域模型的相關命名)DO / BO / DTO / VO等
實際上仍是有可能會存在着諸如:UserVO,UserDTO,UserDAO這樣的命名。對不起,在筆者的團隊中,這樣的命名也會被禁止,這裏分爲2種狀況:編程
禁止使用 VO / BO / DTO / VO 等進行領域模型的命名
有讀者要問,那麼若是萬一項目中要使用DTO或者VO咋辦?筆者的觀點以下:windows
第一,項目中避免使用DTO或者VO,DTO是一個早在2004年就被討論並認定爲一個反模式的東西;設計模式
第二,誰規定領域模型就必定要用DTO或者VO作結尾?還原領域模型的原本意義纔是命名的核心,一個User在實際業務系統中多是一個Admin或者Supervisor,那就直接用Admin來命名,而不是把User轉化成UserVO,UserVO啥都不是,是初級程序員造出來的一個怪胎。
全部的DAO使用正常的駝峯法進行命名,例如:UserDao
對上面這條,或許有不少DAO大寫黨要發飆了。其實筆者就想反問這些人一句:你咋不把UserService寫成UserSERVICE呢?
命名原則
1.1.5 力求語義表達完整清楚,不要嫌名字長 1.1.10 杜絕徹底不規範的縮寫,避免望文不知義
這兩條說的是命名的基本原則,總的來講其實表達了一個意思:你他媽的別給我用縮寫!
其實有不少程序員會很是神奇的患上「縮寫綜合症」。好比很是典型的就是:UserMgmt,這他媽是什麼鬼?多敲幾個字母會死麼?
類的命名
1.1.6 抽象類命名使用Abstract或Base開頭;異常類命名使用Exception結尾;測試類命名以它要測試的類的名稱開始,以Test結尾 1.1.13 對於Service和DAO類,基於SOA的理念,暴露出來的服務必定是接口,內部的實現類用Impl的後綴與接口區別 1.1.13 若是是形容能力的接口名稱,取對應的形容詞作接口名 1.1.14 枚舉類名建議帶上Enum後綴,枚舉成員名稱須要全大寫,單詞間用下劃線隔開 1.1.11 若是使用到了設計模式,建議在類名中體現出具體模式 1.1.9 包名統一使用單數形式;類名若是有複數含義,類名可使用複數形式
上述規則,主要在講述具體命名一個類的時候的一個「用詞規範」。這些用詞規範絕大多數實際上也是一種約定俗成,好比Abstract前綴,Exception後綴等等。
對於接口的命名,筆者最爲不能忍受的一種命名,就是將全部的接口以大寫字母I開頭,諸如:IUserService。真是一種坑爹到極致的命名:第一,IUserService糾結是一個啥玩意兒?好好的UserService,加上一個大寫字母I,直接失去了閱讀時的語義性;第二,誰他媽知道你這東西究竟是大寫字母I仍是數字1啊?
有關枚舉類名是否加上Enum後綴,筆者對此有所保留。在筆者的團隊中,是不使用Enum做爲後綴的,但對此並不反感。至於枚舉成員名稱,不用大寫字母並下劃線隔開的,基本屬於缺心眼行爲。Enum的設計初衷就是對常量的規整和擴展,因此命名規範繼承自常量是比較合理的一種選擇。
在命名中體現設計模式,相信這一點不少程序員都能遵照。由於在筆者看來,能在代碼中熟練使用設計模式的同窗,一般也不會是一個對本身毫無要求的爛貨。這條命名規範在Spring以及不少優秀的開源項目中都有很深入的體現。
類名是否可使用複數形式,相信主要的分歧來自於工具類。筆者的規定是:
提供一系列靜態方法的工具類,一律使用Utils做爲後綴命名
這條規範的依據,主要來自於筆者發現commons和spring這兩個比較優秀的開源框架中提供的工具類一般都帶s結尾。
常量規約
1.2.1 不容許出現任何魔法值(即未經定義的常量)直接出如今代碼中 1.2.3 不要使用一個常量類維護全部常量,應該按常量功能進行歸類,分開維護 1.2.4 常量的複用層次有五層:跨應用共享常量、應用內共享常量、子工程內共享常量、包內共享常量、類內共享常量 1.2.5 若是變量值僅在一個範圍內變化用Enum類。若是還帶有名稱以外的延伸屬性,必須使用Enum類 1.1.12 儘可能不要在接口裏定義變量,若是必定要定義變量,確定是與接口方法相關,而且是整個應用的基礎常量
常量規約的核心有兩點:第一,別使用常量;第二,讓常量可控。
常量的存在按照筆者我的的理解是向下兼容的選擇(由於JDK1.5以後纔出現枚舉)外加用起來足夠爽(想象一下靜態調用時引用的便捷性,甚至基本類型能夠直接參與業務邏輯的計算)。
因此在上述規則中,咱們能夠看到常量進化到枚舉的趨勢,也能看到因爲用起來足夠爽帶來的常量管理需求:要求分組(1.2.3)以及要求放在合適的位置(1.2.4)。
有關分組,筆者有不一樣意見:常量分組未必要分散到不一樣的類,在一個常量類中定義靜態類也是一種分組方式,有時候這樣的分組方式可能管理起來更有效。
至於接口中只能定義常量不能定義變量,基本就屬於幼兒園規則了。
語法糖
1.2.2 long或者Long初始賦值時,必須使用大寫的L,不能是小寫的l,小寫容易跟數字1混淆,形成誤解 1.1.12 接口類中的方法和屬性不要加任何修飾符號(public 也不要加),保持代碼的簡潔性,並加上有效的Javadoc註釋 1.4.2 全部的覆寫方法,必須加@Override註解 1.4.3 可變參數必須放置在參數列表的最後。(提倡同窗們儘可能不用可變參數編程) 1.4.4 對外暴露的接口簽名,原則上不容許修改方法簽名,避免對接口調用方產生影響。接口過期必須加@Deprecated註解,並清晰地說明採用的新接口或者新服務是什麼 1.4.5 不能使用過期的類或方法 1.4.10 序列化類新增屬性時,請不要修改serialVersionUID字段,避免反序列失敗 1.4.17 循環體內,字符串的聯接方式,使用StringBuilder的append 1.4.18 final可提升程序響應效率 1.4.19 慎用Object的clone方法來拷貝對象
有關語法糖的總結其實比較牽強,由於絕大多數的規則看上去都比較小兒科,好比像覆寫方法的@Override註解,@Deprecated註解,可變參數的問題等等基本上都在IDE層面解決了。
當一個項目在IDE中產生了一些因爲使用過期方法之類的事兒致使的warning時,有潔癖的程序員應該主動修復這個warning。這也是是一個程序員的基本素養問題。
最後的三條,筆者認爲有點雞肋,對於初級程序員,大多還到不了考慮final和clone的層次;而中高級程序員,這幾條規則對他們而言並沒有問題。
基本類型
1.2.2 long或者Long初始賦值時,必須使用大寫的L,不能是小寫的l,小寫容易跟數字1混淆,形成誤解 1.4.7 全部的相同類型的包裝類對象之間值的比較,所有使用equals方法比較 1.4.8 全部的POJO類屬性必須使用包裝數據類型 1.4.8 RPC方法的返回值和參數必須使用包裝數據類型 1.4.8 全部的局部變量【推薦】使用基本數據類型
有關基本類型的聲明(1.2.2)和比較(1.4.7),這兩條規則比較直觀,再也不敘述。
而有關基本類型和包裝類型的使用,這東西一直是吵架的核心。用仍是不用?這是個問題!很顯然,阿里同窗的觀點是:爲了提升程序的容錯性和擴展性,儘量使用包裝類型。
從阿里同窗舉的例子來講,也是有必定說服力的:
數據庫的查詢結果多是null,由於自動拆箱,用基本數據類型接收有NPE風險 好比顯示成交總額漲跌狀況,即正負x%,x爲基本數據類型,調用的RPC服務,調用不成功時,返回的是默認值,頁面顯示:0%,這是不合理的,應該顯示成中劃線-。
因此包裝數據類型的null值,可以表示額外的信息
不過筆者認爲,若是程序員對程序可以駕馭得比較好,基本類型也是一種很好的選擇。由於基本類型有一些比較好用的特性:好比說默認值。筆者在這裏也舉個例子進行說明:
一般咱們都會用is_disabled字段在數據庫中表示某一個表的記錄是否被邏輯刪除,而這個字段,在Java代碼中被映射成什麼類型呢? Boolean?若是被映射成包裝類型,那麼數據庫裏面的這個字段就能夠爲null,有些讀者會說,這並無什麼問題啊。但是,數據庫is_disabled字段若是爲null,表明什麼邏輯含義呢?
這條記錄到底是有效仍是無效? 若是這個字段不能爲null,那麼將其映射成基本類型是一個皆大歡喜的事情:既保證了數據庫數據的完整性,咱們在初始化的時候還能夠忽略這個字段,由於boolean天生的默認值就是false
因此,筆者對於包裝類仍是基本類型的結論是:
一切跟着業務的實際狀況而定,基本類型也有其生存空間
方法命名
1.1.15 Service/DAO層方法命名規約 - 獲取單個對象的方法用get作前綴 - 獲取多個對象的方法用list作前綴 - 獲取統計值的方法用count作前綴 - 插入的方法用save(推薦)或insert作前綴 - 刪除的方法用remove(推薦)或delete作前綴 - 修改的方法用update作前綴
有關方法的命名,筆者想多說幾句不一樣意見。對於上述的規則,筆者認爲適合在DAO這個層次進行實踐,而不能應用於Service層。
使用Hibernate做爲持久層框架的讀者,對Hibernate的API應該比較熟悉,而上面的命名規範,和Hibernate對外暴露的API名稱是很接近的。咱們知道,一般到了DAO這個層次,數據庫操做相對來講是一個原子操做,因此增刪改查的語義是最適合作方法命名的。這也就是筆者認爲這套規則在DAO層可以被實踐得很好的一個緣由。
固然,上述規則中有一個例外:
獲取單個對象用load作前綴,避免使用get
緣由很簡單,get多是getter方法的前綴,做爲一個偏執狂,老子不冒風險。
話題回到Service的命名上來,爲何筆者不認同使用相同的命名規範做用於Service層呢?由於Service層一般是對外暴露的接口,具備必定的業務意義,也就是說Service層一般也不會是簡單的增刪改查,而是若干原子操做的集合。
舉兩個很簡單的例子:發短信。發短信這個業務中可能包含了本地配置的讀取、本地數據庫的讀寫,遠程服務的調用。咱們能夠看到這是一連串數據庫操做甚至是異構系統調用的集合實現,能用簡單的增刪改查來命名嗎?因此,筆者的觀點很簡單:
Service層接口方法的命名,應還原業務的原本面目,採用動詞或者動賓結構來進行方法的命名
舉例來講:resetPassword / login / sendMessage 都是比較合理的命名方式。
方法和屬性
1.4.9 定義DO/DTO/VO等POJO類時,不要設定任何屬性默認值 1.4.11 構造方法裏面禁止加入任何業務邏輯,若是有初始化邏輯,請放在init方法中 1.4.14 當一個類有多個構造方法,或者多個同名方法,這些方法應該按順序放置在一塊兒,便於閱讀 1.4.15 類內方法定義順序依次是:公有方法或保護方法 > 私有方法 > getter/setter方法 1.4.16 setter方法中,參數名稱與類成員變量名稱一致,this.成員名=參數名。在getter/setter方法中,儘可能不要增長業務邏輯 1.4.20 類成員與方法訪問控制從嚴
這幾條規約理解起來不難,執行起來也不難。要探究背後的緣由,可能就須要花點功夫。
好比,構造方法和setter/getter方法禁止加入業務邏輯,主要是這些方法有很大機率被程序框架的反射機制調用。一旦其中含有業務邏輯,那麼調試和定位就會變成災難。
不過對於getter方法,一般要網開一面。由於在實際狀況中,咱們每每會在一個POJO中加入額外的getter方法用於序列化或者內部邏輯的使用。在這種狀況下,避免和其餘getter方法產生分歧是須要注意的地方。
至於說到類內的方法定義順序,筆者基本贊成上述規則,但在實際執行中可能更加嚴格:getter和setter方法的順序也有嚴格講究,必須是先getter方法,後setter方法,而不是讓它們成對出現。
有關類成員和方法的訪問控制,阿里的同窗洋洋灑灑說了好幾條,語法層面偏多,在這裏就再也不詳細展開。
格式規約
格式規約是代碼規範中爭議最大的,因爲條目衆多,在這裏就不逐一解讀,挑選幾條大體說一下。
1.3.5 縮進採用4個空格,禁止使用tab字符 1.3 6. 單行字符數限不超過 120 個 1.3.8 IDE的text file encoding設置爲UTF-8; IDE中文件的換行符使用Unix格式,不要使用windows格式 1.3.10 方法體內的執行語句組、變量的定義語句組、不一樣的業務邏輯之間或者不一樣的語義之間插入一個空行
絕大多數狀況下,空格黨和Tab黨的較量是空格黨完勝。筆者也不記得是多少年前被一位前輩教育說禁止使用Tab,就保持了良好的習慣至今。對於縮進,我的比較贊同4個空格,但HTML等頁面上使用4個空格的話,一些複雜頁面的縮進就會比較恐怖,此時能夠降級爲2個空格。
對於單行字符數的限制不超過120個這條規則,筆者徹底不能認同。這裏面牽涉到的狀況比較多,不能一棒子打死了。有些邏輯有大量的分支和循環的嵌套,若是遵循4個空格的縮進原則,碰到方法名稱還比較長的情況,就要折行,這給代碼閱讀帶來極大的困擾;另外有一種狀況,就是須要額外進行比較長的註釋編寫,不能寫在一行裏的感受真是比較糟糕,由於還得考慮斷句纔不影響閱讀。另外,筆者有一個習慣是在條件語句邊上加一句註釋,這樣就有很大機率會超出120字:
有人會問,條件語句邊上加註釋是什麼鬼?從上面的代碼上能夠看到,條件語句上面的一行註釋實際上在解釋整個代碼片斷,而條件語句邊上的註釋說明的是條件語句自己!固然若是讀者有更好的寫註釋的位置,請及時給筆者留言。
文件的UTF-8和Unix格式沒什麼好說的,IDE支持的也很好。但這一點對初級程序員尤其重要,我已經不知道多少次就這個問題懲罰過實習生了。
有關語句組空行,是筆者極力推薦的一個作法。這不只僅是爲了空行而空行,這裏的空行自己就是一種編程思路的整理。而筆者還有一個習慣就是對比較複雜的邏輯,都在語句組的前面加上註釋,註釋也用編號編排,這樣回頭debug時也會極大提高效率。
集合類型
阿里規範中的集合類型這個章節,感受寫得比較雞肋。絕大多數的規範彷佛都是針對初級程序員的。筆者看了半天也沒總結出一條值得額外解讀的,因此權當複習一遍基礎知識就好。
併發處理
1.6.1 獲取單例對象須要保證線程安全,其中的方法也要保證線程安全 1.6.2 建立線程或線程池時請指定有意義的線程名稱,方便出錯時回溯 1.6.3 線程資源必須經過線程池提供,不容許在應用中自行顯式建立線程 1.6.4 線程池不容許使用Executors去建立,而是經過ThreadPoolExecutor去建立
上面這4條規則主要是針對線程的建立和使用。因爲Spring的存在,其實上述狀況不太可能發生。
1.6.5 高併發時,同步調用應該去考量鎖的性能損耗。能用無鎖數據結構,就不要用鎖;能鎖區塊,就不要鎖整個方法體;能用對象鎖,就不要用類鎖 1.6.7 對多個資源、數據庫表、對象同時加鎖時,須要保持一致的加鎖順序,不然可能會形成死鎖 1.6.8 併發修改同一記錄時,避免更新丟失,要麼在應用層加鎖,要麼在緩存加鎖,要麼在數據庫層使用樂觀鎖,使用version做爲更新依據
上面這3條規則主要是針對鎖。不過這幾條規則看上去更像是3道面試題的答案。這3道面試題分別是:
使用鎖同步,有什麼須要注意的地方? 什麼是死鎖?舉例說明什麼狀況會發生死鎖? 什麼是樂觀鎖?什麼是悲觀鎖?分別用在什麼場景?
相信能解答上述面試題的同窗,應該對上面的原則瞭然於心。
1.6.9 多線程並行處理定時任務時,Timer運行多個TimeTask時,只要其中之一沒有捕獲拋出的異常,其它任務便會自動終止運行,使用ScheduledExecutorService則沒有這個問題 1.6.10 使用CountDownLatch進行異步轉同步操做,每一個線程退出前必須調用countDown方法,線程執行代碼注意catch異常,確保countDown方法能夠執行,
避免主線程沒法執行至countDown方法,直到超時才返回結果回溯 1.6.13 volatile解決多線程內存不可見問題。對於一寫多讀,是能夠解決變量同步問題,可是若是多寫,一樣沒法解決線程安全問題 1.6.14 HashMap在容量不夠進行resize時因爲高併發可能出現死鏈 1.6.15 ThreadLocal沒法解決共享對象的更新問題,ThreadLocal對象建議使用static修飾。這個變量是針對一個線程內全部操做共有的,因此設置爲靜態變量,
全部此類實例共享此靜態變量
上面這些規則基本上屬於知識貼範疇,能夠一帶而過,有些可能不太會實際碰到。像定時任務,可能使用Spring的封裝更多一些,而Spring默認就是使用ScheduledExecutorService的。
而CountDownLatch的異常捕獲,也是一個老生常談的問題了,屬於多線程編程的基本功。
最後的三條對於寫應用的同窗接觸很少,但寫底層的同窗應該會很熟悉。
註釋規約
註釋規約的內容比較多,這裏也僅挑選一些具備表明性的進行解讀
1.8.5 全部的枚舉類型字段必需要有註釋,說明每一個數據項的用途 1.8.6 與其"半吊子"英文來註釋,不如用中文註釋把問題說清楚。專有名詞與關鍵字保持英文原文便可 1.8.8 註釋掉的代碼儘可能要配合說明,而不是簡單的註釋掉 1.8.10 好的命名、代碼結構是自解釋的,註釋力求精簡準確、表達到位
枚舉類加註釋是很是必要的,由於枚舉一般是都是常量的擴展,而常量是須要說明的。
鑑於不少程序員的英語水平,筆者建議英語不夠好的程序員直接使用中文寫註釋。
對於註釋掉的代碼,筆者的意見是在絕大多數狀況下應該直接刪除,除非在很短的時間內還有恢復的餘地。
有關什麼是好的命名和代碼結構,什麼樣的命名可以使得代碼作到自解釋,筆者將另外撰文進行說明。
數據庫規約
數據庫規約自己並不屬於Java規約的範疇,不過阿里的規範中包含了很多數據庫規約的內容,因此筆者也一樣加以解讀。
3.1.1 是與否概念的字段,必須使用is_xxx的方式命名,數據類型是unsigned tinyint( 1表示是,0表示否) 3.1 2 表名、字段名必須使用小寫字母或數字;禁止出現數字開頭,禁止兩個下劃線中間只出現數字 3.1.3 表名不使用複數名詞 3.1.4 禁用保留字 3.1.5 惟一索引名爲uk_字段名;普通索引名則爲idx_字段名 3.1.10 表的命名最好是加上業務名稱_表的做用 3.1.11 庫名與應用名稱儘可能一致
上述規約主要說的是庫、表、字段的命名規約。應該說絕大多數的上述規約都是參考項,須要根據實際狀況進行調整,咱們逐條來講。
有關布爾值的數據庫映射,對於使用is_xx進行命名沒有異議,對於數據類型是否應該使用tinyint稍有保留,筆者實際上使用bit更多。因爲布爾值所對應的Java類型是boolean,因此筆者一般在命名時,利用boolean的默認值特性,對一些經常使用的命名進行更加嚴格的規定。好比「是否有效」,命名成爲「is_disabled」就要比「is_enabled」來的好。由於 is_disable = false 是絕大多數程序的事實邏輯,這樣就能夠利用boolean值默認爲false的特性。
Java中的絕大多數命名都使用駝峯法,而數據庫的命名實際上更加嚴格。光光小寫是不夠的,而是要強制使用下劃線命名法(主要是由於SQL是大小寫不敏感的語言)。筆者在實際工做中常常看到使用駝峯法命名錶名或者字段名的,這種基本上屬於小學沒畢業的行爲。
有關表名不能使用複數,不能使用關鍵字,這些屬於比較基礎的命名規範,應該遵照。可是筆者在這裏提出更爲嚴格的要求:不只不能使用SQL關鍵字進行命名,一樣不容許使用Java關鍵字!由於絕大多數狀況,數據庫字段會被映射到相應的Java對象,若是可使用Java關鍵字,那麼映射的時候就是自找麻煩了。
最後三條規約屬於建議,相信每一個公司都有本身獨特的規定。好比筆者見過有一些寫Oracle出身的程序員,習慣使用tbl_作表名的前綴,使用vw_作視圖的前綴。我的以爲這個方面不宜作過多規定,只要團隊保持風格總體一致便可。
3.1.6 小數類型爲decimal,禁止使用float和double 3.1.7 若是存儲的字符串長度幾乎相等,使用char定長字符串類型 3.1.8 varchar是可變長字符串,不預先分配存儲空間,長度不要超過5000
這三條主要說的是數據庫設計時的類型規約。
除了上述三條以外,在筆者團隊另外還會遵照以下幾條:
明確日期和時間,日期使用date類型並使用xxDate進行Java字段命名,時間使用date_time類型並使用xxTime進行Java字段命名,以示區分
上面這條主要是和日期時間有關的,強制這樣的規約,對於提高代碼的可讀性是有幫助的。
枚舉類型在數據庫中既能夠映射成int,也能夠映射成varchar,視實際狀況定
一般對於排序和檢索有強依賴的,枚舉類型映射成int比較理想,不然能夠映射成varchar。雖然從效率上說,int基本上會強於varchar,但varchar畢竟可讀性更好,因此仍是應該一分爲二來看。
3.1.9 表必備三字段:id, gmt_create, gmt_modified 3.1.8 若是存儲長度大於此值,定義字段類型爲text,獨立出來一張表,用主鍵來對應,避免影響其它字段索引效率 3.1.12 若是修改字段含義或對字段表示的狀態追加時,須要及時更新字段註釋 3.1.13 字段容許適當冗餘,以提升性能,可是必須考慮數據同步的狀況 3.1.14 單錶行數超過500萬行或者單表容量超過2GB,才推薦進行分庫分表 3.1.15 合適的字符存儲長度,不但節約數據庫表空間、節約索引存儲,更重要的是提高檢索速度
上面的規約主要涉及到一些數據庫表設計上的原則。
其中,3.1.8是很是值得你們注意的一點,筆者我的的習慣是對於大字段,拆表的同時,優化SQL,儘量作到用主鍵單獨取大字段,避免產生效率瓶頸。
而3.1.14是但願提醒一些自視甚高的架構師不要過早的進行過分設計。這裏筆者提一點:
對於每一張數據庫表的設計,應該預估表在將來若干時間段內的數量,以採起最佳的程序處理措施
這裏所說的最佳程序處理措施包括並不限於:使用應用級別緩存對數據庫進行減壓;選取合適的時間點對錶進行分庫分表;是否進行人爲拆表以保證較快的SQL執行等等。
有關3.1.13,咱們在有關SQL編寫環節還會說到。
3.2.1 業務上具備惟一特性的字段,即便是組合字段,也必須建成惟一索引 3.2.2 超過三個表禁止join 3.2.3 在varchar字段上創建索引時,必須指定索引長度 3.2.4 頁面搜索嚴禁左模糊或者全模糊 3.2.5 若是有order by的場景,請注意利用索引的有序性 3.2.7 利用延遲關聯或者子查詢優化超多分頁場景 3.2.9 建組合索引的時候,區分度最高的在最左邊
上述規約主要講的是和索引相關的內容。對於這塊,筆者不是專業的DBA,因此只是挑了其中和程序開發特別有關的來說一講。
好比3.2.2的禁止超過3個表的join,在筆者的團隊中,規定更爲嚴格:
禁止超過2個表的join語句出如今程序中
其實不準使用join是不少初級程序員很是不能理解的。要說明白這個問題,估計又要長篇大論,筆者會另闢文章進行說明。但這裏仍是引用一下robbin的觀點(筆者表示深入贊同):
另外有關嚴禁使用全模糊查找,建組合索引時,區分度最高的往左放這些原則,在必定程度上會改變咱們編寫程序的習慣,因此應該時刻注意。
3.3.1 不要使用count(列名)或count(常量)來替代count(*) 3.3.5 在代碼中寫分頁查詢邏輯時,若count爲0應直接返回,避免執行後面的分頁語句 3.3.6 不得使用外鍵與級聯,一切外鍵概念必須在應用層解決 3.3.7 禁止使用存儲過程,存儲過程難以調試和擴展,更沒有移植性 3.3.9 in操做能避免則避免,若實在避免不了,須要仔細評估in後邊的集合元素數量,控制在1000個以內 3.4.1 在表查詢中,一概不要使用 * 做爲查詢的字段列表,須要哪些字段必須明確寫明
上面的這幾條屬於SQL編寫規約。阿里的規範中洋洋灑灑講了不少條,實際上都是在給程序員提個醒,筆者在這裏不在贅述
有關count(*)的爭論,一直有大量的說法。這次阿里的規範總算爲count(*)黨找到了SQL標準,應該說也基本爲這件事情畫上了句號。
有關外鍵和級聯,筆者稍有困惑的是外鍵。由於按照筆者的理解,外鍵影響數據庫插入的速度應該有限,與外鍵約束帶來的好處相比,或許仍是有外鍵更好一些(有這方面經驗的讀者能夠留言指點迷津)。級聯是惡魔,必須禁止。
至於存儲過程,或許Oracle出身的DBA會跳出來唱反調了。筆者的觀點和阿里相同:存儲過程很難移植和維護,應該拋棄。
有關表查詢中不準使用 * 做爲查詢的字段列表,這點或許可以成爲規約,但筆者並不十分認同。尤爲是對於使用Hibernate做爲ORM工具的同窗來講,這條規則執行起來有難度。
代碼風格
1.7.1 在一個switch塊內,每一個case要麼經過break/return等來終止,要麼註釋說明程序將繼續執行到哪個case爲止;
在一個switch塊內,都必須包含一個default語句而且放在最後,即便它什麼代碼也沒有
這條主要是期待程序員人爲把握好代碼的執行邏輯。對於switch語句,若是沒有終止語句,會依次執行每個case塊。實際上,筆者認爲switch語句是一個比較差的語法糖,一般狀況下均可以用更加優雅的方式來寫,包括並不限於使用設計模式。因此在筆者的團隊中是禁止使用switch語句的。
1.7 2 在if/else/for/while/do語句中必須使用大括號,即便只有一行代碼,避免使用下面的形式:if (condition) statements;
這一條比較有意思,由於這種一行式的代碼風格,在javascript裏面常常會看到,因此不少全棧工程師也會把它引入到Java中來。筆者對此並不反感,但確實在可讀性上不那麼友好。
1.7.3 推薦儘可能少用else, if-else的方式能夠改寫成: if(condition){ ... return obj; } // 接着寫else的業務邏輯代碼; 1.7.3 若是非得使用if()...else if()...else...方式表達邏輯,【強制】請勿超過3層,超過請使用狀態設計模式
上面這點筆者比較認同,由於else不只會帶來大段的代碼縮進的困擾,同時也會下降代碼的可讀性。不過對於那些堅持必須在代碼的最後一行統一return的同窗,上面的寫法可能就不太容易接受了。實際上,上述代碼結構比較常見於Spring的源碼中,倒不是儘早return,而是else的邏輯塊可能直接throw異常出去了。
1.7.4 除經常使用方法(如getXxx/isXxx)等外,不要在條件判斷中執行其它複雜的語句,將複雜邏輯判斷的結果賦值給一個有意義的布爾變量名,以提升可讀性 boolean existed = (file.open(fileName, "w") != null) && (...) || (...); if (existed) { ... }
有關這一條,補充說明一下:將複雜的邏輯判斷結果賦值給一個有意義的布爾變量名,除了提升可讀性以外,實際上可以極大方便調試。但筆者認爲單單只是抽取部分代碼,並不能提升可讀性,而是應該將複雜的邏輯判斷進一步封裝爲一個方法:
上面的代碼片斷中,左邊是阿里風格,右邊是陸老師的風格,你們能夠比較一下,哪一個更好?哪一個更符合面向對象的思惟呢?
1.7.5 循環體中的語句要考量性能,如下操做盡可能移至循環體外處理,如定義對象、變量、獲取數據庫鏈接,進行沒必要要的try-catch操做
這一條值得說一下,由於有些代碼會走得比較深,寫着寫着就忘了它處於循環體的內部了。因此保持一個謹慎的心態比較重要。
1.7.7 方法中須要進行參數校驗的場景: 1) 調用頻次低的方法。 2) 執行時間開銷很大的方法,參數校驗時間幾乎能夠忽略不計,但若是由於參數錯誤致使中間執行回退,或者錯誤,那得不償失。 3) 須要極高穩定性和可用性的方法。 4) 對外提供的開放接口,無論是RPC/API/HTTP接口。 5) 敏感權限入口。 1.7.8 方法中不須要參數校驗的場景: 1) 極有可能被循環調用的方法,不建議對參數進行校驗。但在方法說明裏必須註明外部參數檢查 2) 底層的方法調用頻度都比較高,通常不校驗。畢竟是像純淨水過濾的最後一道,參數錯誤不太可能到底層纔會暴露問題。通常DAO層與Service層都在同一個應用中,
部署在同一臺服務器中,因此DAO的參數校驗,能夠省略 3) 被聲明成private只會被本身代碼所調用的方法,若是可以肯定調用方法的代碼傳入參數已經作過檢查或者確定不會有問題,此時能夠不校驗參數
這兩條說的是參數校驗,說的比較在理,也比較全面。比起不少公司的奇葩規定來講,要人性化得多。筆者認爲須要補充的是:參數的校驗主要還須要從格式和業務兩個層面進行考量。業務層面的校驗每每要比單純的格式校驗更爲複雜,因此在寫代碼時能夠創建必定層次的假設,固然這可能也會引入團隊溝通的問題,須要根據實際狀況權衡。
有關阿里代碼風格方面的解讀,受限於阿里自身提出的規約比較少。對此,筆者是稍有失望的。由於代碼風格規約是最可以體現一個團隊對於代碼整潔程度的一個衡量標準。因此筆者忍不住在這裏多加了幾條筆者團隊的共識,供讀者參考:
在任何狀況下,代碼量越少越容易維護
基於上面的原則,筆者的團隊會鼓勵使用三目表達式對簡要的if/else進行重構
固然,像下列左側的代碼,也會重構成右側的:
一個複雜的Service層邏輯,不該超過30行,不然須要進行邏輯規整和抽象
在業務邏輯中儘量不要使用setter方法,而是使用構造函數或者封裝成一個有邏輯意義的方法,提升代碼的可讀性
什麼?連setter方法都不讓用?這是什麼SB同樣的規約啊!事實上,筆者團隊確實是這麼作的,咱們來看一下代碼:
在上面的代碼中,左側代碼中的setter方法調用,會被封裝到ShuttleOrder對象中的cancel方法中去。在實際的service代碼中,只會出現下半部分的一行代碼。
這樣作的好處在於:cancel這個方法被封裝後,shuttleOrder.cancel()的調用從可讀性上要明顯優於使用2句setter方法,同時也爲未來的邏輯擴展預留了位置。這也是面向對象的一種實踐。
轉載自:https://www.cnblogs.com/winner-0715/p/7594254.html