Java API設計原則清單

在設計Java API的時候老是有不少不一樣的規範和考量。與任何複雜的事物同樣,這項工做每每就是在考驗咱們思考的縝密程度。就像飛行員起飛前的檢查清單,這張清單將幫助軟件設計者在設計Java API的過程當中回憶起那些明確的或者不明確的規範。本文也能夠看做爲「API設計指南」這篇文章的附錄。html

咱們還準備了一些先後比對的例子來展現這個列表如何幫助你理清設計需求,找出錯誤,識別糟糕的設計實踐以及如何尋找改進的時機。java

這個清單使用了以下的語言規範:web

要 - 表示必要的設計算法

建議 - 表示在幾個最好的設計中選擇一個api

考慮 - 表示一個可能的設計上的改進數組

避免 - 表示一個設計上的缺陷ide

不要 - 表示一個設計上的錯誤函數

1. 包設計清單性能

1.1. 共通單元測試

  • 1.1.1.    建議把API和實現放入不一樣的包

  • 1.1.2.    建議把API放進上層包,而把實現放進下層包

  • 1.1.3.    考慮把一組大型的API分拆進不一樣的包

  • 1.1.4.    考慮把API和實現打包進不一樣的jar包

  • 1.1.5.    避免API的實現類之間的內部依賴

  • 1.1.6.    避免把API分拆了太細

  • 1.1.7.    避免把公共實現類放到API的包中

  • 1.1.8.    不要在調用者和實現類之間創建依賴

  • 1.1.9.    不要把沒有關係的API放進同一個包

  • 1.1.10.  不要把API和SPI(service provider interface)放進一個包(注:二者不一樣能夠查看這個頁面http://stackoverflow.com/questions/2954372/difference-between-spi-and-api )

  • 1.1.11.  不要移動或者重命名一個已經發布的公共API

1.2. 命名

  • 1.2.1.    (一級)包名以公司(或者組織)的根命名空間來命名

  • 1.2.2.    使用一個穩定的產品名稱或者一個產品系列的名稱做爲包的二級名稱

  • 1.2.3.    使用API名稱做爲包名的(三級名稱)結尾

  • 1.2.4.    考慮把僅包含實現的包的名稱中包含"internal"這個詞(注:彷佛「impl」更常見一些)

  • 1.2.5.    避免使用組合起來的名稱

  • 1.2.6.    避免包名和包內的類名使用一樣的名稱

  • 1.2.7.    避免在包名稱中使用「api」這個詞

  • 1.2.8.    不要使用營銷,計劃,組織單元(部門)和地理名稱

  • 1.2.9.    不要在包名中使用大寫字母

1.3. 文檔

  • 1.3.1.    爲每個包提供一個package.html

  • 1.3.2.    遵循標準的javadoc的規範

  • 1.3.3.    在API的開始處用一句短小的話來歸納(描述)

  • 1.3.4.    提供足夠多的細節來幫助判斷是否須要使用和如何使用該API

  • 1.3.5.    指出該API的入口(主要的類或者方法)

  • 1.3.6.    包含覆蓋主要的,基本功能演示的樣例代碼

  • 1.3.7.    包含一個指向開發者指南的超連接

  • 1.3.8.    包含一個指向手冊的超連接

  • 1.3.9.    指出相關的API集合

  • 1.3.10.  包含API的版本號

  • 1.3.11.  用 @deprecated 標記出再也不使用的API版本

  • 1.3.12.  考慮添加一個版權聲明

  • 1.3.13.  避免過長的包概述

  • 1.3.14.  不要在發佈的javadoc中包含實現相關的包

2. 類型設計清單(這裏的「類型」我的理解爲一組Api)

2.1. 共通

  • 2.1.1.    確保每種(設計的)類型都有單一明確的目的

  • 2.1.2.    確保每種類型表明了(業務)領域的概念,而不是爲了(技術上)的抽象

  • 2.1.3.    限制類型的總數量

  • 2.1.4.    限制類型的大小

  • 2.1.5.    設計相關的類型時保持和原有的類型的一致性

  • 2.1.6.    建議爲多種public的類型提供多種(private)的實現

  • 2.1.7.    建議接口的實現類和繼承關係的類應該在行爲上保持一致性

  • 2.1.8.    建議用抽象類而不是接口解耦Api的實現

  • 2.1.9.    建議使用枚舉而不是常量

  • 2.1.10.  考慮使用泛型

  • 2.1.11.  考慮在泛型參數上增長約束

  • 2.1.12.  考慮使用接口來實現多繼承的效果

  • 2.1.13.  避免爲使用者的擴展(需求)進行設計

  • 2.1.14.  避免深度的繼承層次

  • 2.1.15.  不要使用public內嵌的類型

  • 2.1.16.  不要申明public和protected的變量

  • 2.1.17.  不要把實現的繼承關係暴露給使用者

2.2. 命名

  • 2.2.1.    使用名詞或者名詞詞組

  • 2.2.2.    使用PascalCasing(駝峯命名法的別稱詳見http://en.wikipedia.org/wiki/CamelCase )

  • 2.2.3.    縮寫僅第一個首字母大寫

  • 2.2.4.    爲類型的實際做用使用精確的名稱命名

  • 2.2.5.    爲最經常使用的類型準備最短最容易記憶的名稱

  • 2.2.6.    全部異常都以「Exception」結尾

  • 2.2.7.    使用名詞的單數(好比用Color而不用Colors)來命名枚舉類型

  • 2.2.8.    考慮較長的名稱

  • 2.2.9.    考慮派生類用基類的名稱結尾

  • 2.2.10.  考慮爲抽象類名稱使用「Abstract」開頭

  • 2.2.11.  避免使用縮略語

  • 2.2.12.  避免太通用的名詞

  • 2.2.13.  避免同義詞

  • 2.2.14.  避免在相關的Api中使用類型的名稱

  • 2.2.15.  不要使用僅大小寫不一樣的名稱

  • 2.2.16.  不要使用前綴

  • 2.2.17.  不要以「I」做爲接口的名稱開頭

  • 2.2.18.  不要(重複)使用Java核心包中的名稱

2.3. 類

  • 2.3.1.    最小化實現使用的依賴

  • 2.3.2.    先列出public方法

  • 2.3.3.    申明實現方法爲private(這裏是筆誤嗎?)

  • 2.3.4.    爲一個public抽象類定義至少一個public的shi

  • 2.3.5.    爲基本的使用狀況提供足夠的缺省實現

  • 2.3.6.    設計基本上不變的類

  • 2.3.7.    把無狀態,訪問器,擴展(mutator我的理解爲多種參數形式的方法)方法集合到一塊兒

  • 2.3.8.    把擴展方法的數量控制到最少

  • 2.3.9.    考慮設計一個默認的無參的構造方法

  • 2.3.10.  考慮重寫equal,hashCode方法

  • 2.3.11.  考慮實現Comparable接口

  • 2.3.12.  考慮實現Serializable接口

  • 2.3.13.  考慮使類能夠容易的擴展

  • 2.3.14.  考慮申明類爲final

  • 2.3.15.  考慮爲類的實例化提供一個public的構造方法

  • 2.3.16.  考慮使用自定義的類型來加強類的不可變性

  • 2.3.17.  考慮設計不可變的類

  • 2.3.18.  避免靜態類

  • 2.3.19.  避免使用Cloneable

  • 2.3.20.  不要向靜態類中添加實例duixi

  • 2.3.21.  不要爲使用者不該擴展的public抽象類提供public的構造方法

  • 2.3.22.  不要濫用初始化

2.4. 接口

  • 2.4.1.    爲每個public接口提供至少一個實現類

  • 2.4.2.    爲每個public接口設計至少一個消費方法

  • 2.4.3.    不要對一個已經發布的public接口添加新的方法

  • 2.4.4.    不要使用標記接口(標記接口詳見http://en.wikipedia.org/wiki/Marker_interface_pattern )

  • 2.4.5.    不要把public接口設計成常量的容器(這個實在很常見啊……)

2.5. 枚舉

  • 2.5.1.    考慮爲枚舉類型指定一個0值(「NONE」或者「Unspecialized」等等)

  • 2.5.2.    避免只有一個值的枚舉

  • 2.5.3.    不要使用枚舉實現開放式的值集合

  • 2.5.4.    不要爲未來可能增長的值設計枚舉

  • 2.5.5.    不要爲已經發布的版本增長新的枚舉值

2.6. 異常

  • 2.6.1.    確保自定義的異常能夠被序列化

  • 2.6.2.    考慮爲每種類型定義一個不一樣的異常

  • 2.6.3.    考慮爲代碼訪問提供更多的異常信息

  • 2.6.4.    避免深層的異常繼承

  • 2.6.5.    不要從Exception和RuntimeException之外的類派生自定義異常

  • 2.6.6.    不要直接從Throwable派生異常

  • 2.6.7.    不要在異常信息內包含敏感信息

2.7. 文檔

  • 2.7.1.    爲每種類型(的Api)配上概述

  • 2.7.2.    遵循標準Javadoc的約定

  • 2.7.3.    每種類型開頭以一句短小的話概述

  • 2.7.4.    爲是否使用以及如何使用該類型提供足夠的細節來幫助作決定

  • 2.7.5.    解釋如何實例化一個類型

  • 2.7.6.    爲一個類型的主要的使用情景提供樣例代碼

  • 2.7.7.    包含指向到開發指南的連接

  • 2.7.8.    包含指向手冊的連接

  • 2.7.9.    顯示相關的類型

  • 2.7.10.  用@deprecated標籤申明過期的類型

  • 2.7.11.  文檔類具備不可變性

  • 2.7.12.  避免冗長的類概述

  • 2.7.13.  不要爲私有方法生成Javadoc

3. 方法設計清單

3.1. 共通

  • 3.1.1.    確保每一個方法實現一個目的

  • 3.1.2.    確保相關的方法都是一個粒度級別的

  • 3.1.3.    確保沒有混合調用方法的公共代碼

  • 3.1.4.    使全部方法的調用具備原子性(原子性:http://jiangyongyuan.iteye.com/blog/364010

  • 3.1.5.    設計protected方法時要像public方法同樣慎重

  • 3.1.6.    限制擴展方法的數量

  • 3.1.7.    設計擴展方法須要具備較強的穩定性

  • 3.1.8.    建議爲一系列重載的方法設計一個泛型的方法

  • 3.1.9.    考慮使用泛型方法

  • 3.1.10.  考慮設計方法對,即兩個方法的做用是相反的

  • 3.1.11.  避免「helper」方法

  • 3.1.12.  避免長時間執行的方法

  • 3.1.13.  避免調用者在普通使用中須要手動寫循環

  • 3.1.14.  避免可選的參數影響方法的行爲

  • 3.1.15.  避免不可重複調用的方法

  • 3.1.16.  不要刪除一個已經發布的方法

  • 3.1.17.  不要在沒有提供替換方法前把一個已經發布的方法標記爲過期

  • 3.1.18.  不要修改一個已經發布的方法的簽名

  • 3.1.19.  不要修改一個已經發布的方法的可觀測行爲(也許指的是輸出之類)

  • 3.1.20.  不要增長一個已經發布方法的調用條件

  • 3.1.21.  不要減小一個已經發布方法的調用結果

  • 3.1.22.  不要爲已經發布的public接口新增方法

  • 3.1.23.  不要爲已經發布的Api新增重載

3.2. 命名

  • 3.2.1.    用給力的,有表達力的動詞做爲名稱起始

  • 3.2.2.    使用駝峯命名法(好奇怪,前面寫的是PascalNaming)

  • 3.2.3.    爲JavaBean的私有屬性預留「get」「set」「is」等訪問方法

  • 3.2.4.    使用對調用者熟悉的詞語

  • 3.2.5.    儘可能使用英語口語

  • 3.2.6.    避免使用縮略語

  • 3.2.7.    避免使用通常的動詞

  • 3.2.8.    避免同義詞

  • 3.2.9.    不要使用「黑話」

  • 3.2.10.  不要依靠參數的名稱和類型判斷方法的意義

3.3. 參數

  • 3.3.1.    爲參數選擇最合適的類型

  • 3.3.2.    在相關方法的調用中對參數爲null值的處理保持一致性

  • 3.3.3.    在相關方法中參數的名稱,類型和順序須要保持一致

  • 3.3.4.    在參數列表中把輸出的參數放到輸入參數以後

  • 3.3.5.    爲重載的方法省略經常使用的默認參數以提供一個較短的參數列表

  • 3.3.6.    在無關的類型中爲相同語義的操做提供重載方法

  • 3.3.7.    建議使用接口而不是具體類做爲參數

  • 3.3.8.    建議使用集合而不是數組做爲參數和返回值

  • 3.3.9.    建議使用通常集合而不是原始(無類型)集合

  • 3.3.10.  建議使用枚舉而不是Boolean或者Integer做爲參數

  • 3.3.11.  建議把單個的參數放到集合或者數組參數以前

  • 3.3.12.  建議把自定義類型的參數放大Java標準類型參數以前

  • 3.3.13.  建議把對象類型的參數方法值類型的參數以前

  • 3.3.14.  建議使用接口而不是具體類做爲返回值

  • 3.3.15.  建議把空的集合而不是null做爲返回值

  • 3.3.16.  建議把返回值設計成能夠做爲其餘方法的合法輸入參數

  • 3.3.17.  考慮爲不可變參數設計一個副本

  • 3.3.18.  考慮在內部存儲弱引用的對象

  • 3.3.19.  避免參數數量變動

  • 3.3.20.  避免參數長度太長(超過3個)

  • 3.3.21.  避免連續的同類型的參數

  • 3.3.22.  避免用做輸出或者輸入輸出的參數

  • 3.3.23.  避免方法重載

  • 3.3.24.  避免參數類型暴露實現細節

  • 3.3.25.  避免boolean參數

  • 3.3.26.  避免返回null

  • 3.3.27.  除了Java核心Api,避免把類型做爲不相關的Api的返回值

  • 3.3.28.  避免把可變的內部對象做爲返回值來引用

  • 3.3.29.  不要把預先設置的常量做爲整型值參數使用

  • 3.3.30.  不要爲未來的(擴展設計)考慮預留參數

  • 3.3.31.  不要在重載方法中改變參數的名稱的順序

3.4. 異常處理

  • 3.4.1.    只有在異常狀況下才拋出異常

  • 3.4.2.    只須要爲可恢復的錯誤拋出已確認的異常

  • 3.4.3.    爲了通知Api使用錯誤而拋出運行時異常

  • 3.4.4.    在適當的抽象層次拋出異常

  • 3.4.5.    進行運行時預置條件的檢查

  • 3.4.6.    爲一個被不能爲null的參數拋出空指針異常

  • 3.4.7.    爲一個除爲null之外異常值的參數排除非法參數異常

  • 3.4.8.    爲一個錯誤上下文環境中的方法調用拋出非法狀態異常

  • 3.4.9.    在錯誤信息中顯示出參數的預置條件

  • 3.4.10.  確保失敗的方法調用不會產生單向的後果

  • 3.4.11.  爲回調方法中的禁止使用的Api提供運行時檢查

  • 3.4.12.  建議優先使用Java標準異常

  • 3.4.13.  建議提供拋出異常的條件的查詢方法

3.5. 重寫

  • 3.5.1.    使用@Override註解

  • 3.5.2.    維持或弱化預置條件

  • 3.5.3.    維持或者增強後置條件(很差翻譯,大概output+effect的意思)

  • 3.5.4.    維持或者增強不可變性

  • 3.5.5.    不要拋出新增的運行時異常

  • 3.5.6.    不要更改方法的類型(無狀態,訪問器或者擴展方法等)

3.6. 構造方法

  • 3.6.1.    最小化構造方法中的工做

  • 3.6.2.    爲全部的屬性設置合理的默認值

  • 3.6.3.    僅把構造方法的參數做爲一種設置參數的快捷方法

  • 3.6.4.    校驗構造方法的參數

  • 3.6.5.    以參數相應的屬性爲其命名

  • 3.6.6.    當提供了多個構造方法時,遵循指南對其進行重載

  • 3.6.7.    建議使用構造方法而不是靜態的工廠方法

  • 3.6.8.    考慮使用無參的構造方法

  • 3.6.9.    若是不是總須要新的實例,考慮使用靜態的工廠方法

  • 3.6.10.  若是你須要在運行時決定一個合適的類型,考慮使用靜態的工廠方法

  • 3.6.11.  若是你須要訪問外部的資源,考慮使用靜態的工廠方法

  • 3.6.12.  當面臨很是多的參數的時候,考慮使用生成器(builder)

  • 3.6.13.  當須要迴避直接實例化類的時候使用考慮private的構造函數

  • 3.6.14.  避免建立非必需的對象

  • 3.6.15.  避免finalizer

  • 3.6.16.  不要從無參的構造方法中拋出異常

  • 3.6.17.  不要向一個已經發布的類中添加顯示的構造方法

3.7. Setters和getters

  • 3.7.1.    以get開頭命名一個返回值不爲boolean的訪問屬性的方法

  • 3.7.2.    以is,can開頭命名一個返回值爲boolean的訪問屬性的方法

  • 3.7.3.    以set開頭命名一個更新本地變量的方法

  • 3.7.4.    校驗setter方法的參數

  • 3.7.5.    最小化getter和setter方法的工做

  • 3.7.6.    考慮從一個getter方法中返回不可變的集合

  • 3.7.7.    考慮實現一個private接口的集合替代public的集合屬性

  • 3.7.8.    考慮只讀的屬性

  • 3.7.9.    設置可變類型的屬性時考慮Defensive Copy(Defensive Copy詳見:http://www.javapractices.com/topic/TopicAction.do?Id=15 )

  • 3.7.10.  當返回可變類型的屬性時考慮Defensive Copy

  • 3.7.11.  getter方法避免返回數組

  • 3.7.12.  避免根據方法內信息沒法完成的校驗

  • 3.7.13.  不要從getter方法中拋出異常

  • 3.7.14.  不要設計只能set的屬性方法 (僅有public的setter而沒有public的getter)

  • 3.7.15.  不要依賴屬性設置的順序

3.8. 回調

  • 3.8.1.    設計時使用最嚴密的預置條件

  • 3.8.2.    設計時使用最弱的後置條件

  • 3.8.3.    考慮傳遞引用對象的方法中把回調接口做爲第一個參數

  • 3.8.4.    避免有返回值的回調方法

3.9. 文檔

  • 3.9.1.    爲每一個方法提供Javadoc註釋

  • 3.9.2.    遵循標準的Javadoc約定

  • 3.9.3.    每一個方法以一句短小的話做爲概述

  • 3.9.4.    申明相關的方法

  • 3.9.5.    用@deprecated標籤申明過期的類型

  • 3.9.6.    顯示全部過期方法的替換方法

  • 3.9.7.    避免冗長的zhus

  • 3.9.8.    包含經常使用的使用模式

  • 3.9.9.    (若是容許的話)包含null值的確切含義

  • 3.9.10.  包含方法的類型 (無狀態,訪問器或者擴展)

  • 3.9.11.  包含方法的預置條件

  • 3.9.12.  包含算法實現的性能特徵

  • 3.9.13.  包含遠程方法調用

  • 3.9.14.  包含訪問外部資源的方法

  • 3.9.15.  包含哪些API能夠在回調中使用

  • 3.9.16.  考慮爲了描述方法的行爲而包含單元測試

 

英文原文:http://theamiableapi.com/2012/01/16/java-api-design-checklist/

相關文章
相關標籤/搜索